C语言-基础入门-学习笔记(8):函数中的变量
一. 生存期和作用域
C程序中的所有变量都有一定的生存期和作用域。生存期是指程序运行时,变量占有内存的时间。作用域是指变量在程序中被使用的有效代码区域。
1.变量的生存期
生存期是指程序运行时变量占有内存的整个时期。当程序运行到变量定义语句时,编译器为其分配内存,这是它的开始,当变量占用的内存被释放时,则为生存期的结束。
范例1
#include <stdio.h>
int reverse(int a){
return -a;
}
int square(int b){
return b*b;
}
int main(void){
int key = 1;//key的生存期开始
printf("%d\n",reverse(key));
printf("%d\n",square(key));
//key的生存期结束
return 0;
}
2.变量的作用域
作用域是指代码可以被使用的代码区域。在变量作用域外使用变量是非法操作。
{ //程序块A开始
int m = 2;
{ //程序块B开始
int n = 3;
m = 9;
} //程序块B结束
{ //程序块C开始
n = 7;
++m;
} //程序块C结束
printf("m=%d,n=%d\n",m,n);
} //程序块A结束
程序块A为程序块B和程序块C的上层程序块
程序块B和程序块C互为外程序块,并且都是程序块A的下层程序块
变量m为程序块A的内部变量,为程序块B和程序块C的外部变量
变量n为程序块A和程序块B的内部变量,为程序块C的外部变量
程序块A中定义的变量m可以在本层程序中使用,可以在下层程序块B中使用,也可以在下层程序块C中使用,但程序块B中的内部变量n则只能在程序块B中使用。
二. 局部变量和全局变量
1.局部变量
局部变量是指作用域无法涵盖整个代码区的变量。局部变量的作用域不能超过所属函数体。
作为一个临时变量,局部变量的主要优点是仅在需要时编辑器才会为其分配内存。
函数的形参也是该函数内的局部变量。
范例2
#include <stdio.h>
void print_value(void){
int var = 1;
++var;
printf("var=[%d]\n",var);
}
int main(void){
printf("Call print_value() at first time:\n");
print_value();
printf("Call print_value() at second time:\n");
print_value();
/*在这里使用var变量是错误的
++var;
*/
return 0;
}
2.全局变量
在所有函数外部定义的变量,即所有函数的外部变量,则被称为“全局变量”。
全局变量为所有函数公用,而且每个函数对全局变量值的改变会保存在内存中。
范例3
#include <stdio.h>
int g_var;
void print_value(void){
++g_var;
printf("g_var = [%x]\n",g_var);
}
int main(){
printf("Call print_value() at first time:\n");
print_value();
printf("Call print_value() at second time:\n");
print_value();
++g_var;
printf("g_var = [%x]\n",g_var);
}
3.初始化全局变量
如果局部变量没有被显示地初始化,编译器不会自动为其清零;如果全局变量没有为其显式初始化,编译器会自动为其初始化,将其内存空间清除归零。
范例4
#include <stdio.h>
#define SIZE 12
//定义两个全局变量
float g_arrayFloat[SIZE];
int g_valueInt;
void print_array(const float a[SIZE]){
int i = 0;
for(i = 0;i<SIZE;i++){
if(SIZE/2 == i)
printf("\n\t");
printf("%2.2f\t",a[i]);
}
printf("\n");
}
int main(void){
//定义两个局部变量
float arrayFloat[SIZE];
int valueInt;
//输出全局变量的初始值
printf("Globle variable: g_arrayFloat\n\t");
print_array(g_arrayFloat);
//输出局部变量的初始值
printf("Local variable: arrayFloat\n\t");
print_array(arrayFloat);
printf("Globle variable: g_valueInt = %d\n",g_valueInt);
printf("Local variable: valueInt = %d\n",valueInt);
return 0;
}
如果在整个程序中都需要使用的变量,建议将其设为全局变量,使得作用域覆盖整个程序;如果只需要在某一小块进行变量的使用,那么建议定义该程序块中的局部变量,使其作用域仅为此程序块,这样在该子程序块执行完成后会释放变量内存。
在变量命名时不同函数内可以使用同样名字的变量,彼此之间互不干扰。但是当本层程序块与上层程序块或与下层程序块的变量名发生重叠时,会出现命名冲突。高层程序块的同名变量会被底层程序块的同名变量屏蔽。
名字一样但类型不同的变量也是同名变量。
范例5
#include <stdio.h>
int max = 14;//定义全局变量
int call_max(const int a,const int b){
int max = a;
if(a < b)
max = b;
printf("In call_max():\n");
printf("\t&max = [%p]\n",&max);
printf("\t&a = [%p]\n",&a);
printf("\t&b = [%p]\n",&b);
return max;
}
void print_max(void){ //没有重新定义max
printf("In print_max():\n");
printf("\tmax = [%d]\n",max);
printf("\t&max = [%p]\n",&max);
}
void increase_max(void){ //没有重新定义max
++max; //当没有重新定义max时,max就是全局变量的max
printf("In increase_max():\n");
printf("\tmax = [%d]\n",max);
printf("\t&max = [%p]\n",&max);
}
int main(void){ //main中的变量a和b与call_max中的a.b互为外部函数,分别有各自的地址,互不相关
const int a = 7;
const int b = 2;
int max = 0;
max = call_max(a,b);
printf("In main():\n");
printf("\t&max = [%p]\n",&max);
printf("\t&a = [%p]\n",&a);
printf("\tb& = [%p]\n",&b);
print_max();
increase_max();
return 0;
}
4.局部变量和全局变量的对比:
1、定义同时没有初始化,则局部变量的值是随机的,而全局变量的值是默认为0.
2、使用范围上:全局变量具有文件作用域,而局部变量只有代码块作用域。
3、生命周期上:全局变量是在程序开始运行之前的初始化阶段就诞生,到整个程序结束退出的时候才死亡;而局部变量在进入局部变量所在的代码块时诞生,在该代码块退出的时候死亡。
4、变量分配位置:全局变量分配在数据段上,而局部变量分配在栈上。
判断一个变量能不能使用,有没有定义,必须注意两点:第一,该变量定义的作用域是否在当前有效,是否包含当前位置;第二,变量必须先定义后使用。所以变量引用一定要在变量定义之前
基本概念:
作用域:起作用的区域,也就是可以工作的范围。
代码块:所谓代码块,就是用{}括起来的一段代码。
数据段:数据段存的是数,像全局变量就是存在数据段的
代码段:存的是程序代码,一般是只读的。
栈(stack):先进后出。C语言中局部变量就分配在栈中。
三. 变量的存储类别
C语言中的变量都有两个属性:数据类型和存储类别。
C语言中定义了4个关键字作为变量的存储类别的修饰词,分别为:auto、static、register和extern。
变量的存储类别决定了变量在内存中的存储区域。
1.内存存储区
1.栈
栈是由编译器管理的动态存储区域,用于存储临时变量,即只在需要时才被分配内存,不需要时编译器会自动回收。(函数形参、局部变量、其他临时变量)
2.堆
堆是由程序管理的动态存储区域,用于分配由程序使用malloc函数申请的内存空间,需要由程序自行释放。
3.静态存储区
静态存储区用于存储全局变量,该区域的内存在程序开始时就已固定分配完毕,直到程序结束由编译器自动释放,在该区域分配的内存在整个程序执行过程中都是有效的。
4.常量存储区
常量存储区用于存储程序中的常量,同时经编译器优化后的const常量也可能占用该区域内存。
2.auto变量
存储类别auto的作用是声明变量的生存期为自动型,auto变量的内存由编译器自动从栈上分配,因此auto变量是临时变量。
auto 数据类型 变量名;
数据类型 auto 变量名;
所有的局部变量的声明中如果不含存储类别,都默认为auto型变量。
const auto int amount = 1;
不能在函数外部声明auto型变量。
范例6
#include <stdio.h>
auto int key;
int main(void){
printf("key = %d\n",key);
return 0;
}
这里将变量key声明为auto变量,又因为该变量定义存在于函数外部,因此是全局变量,产生冲突。
3.static变量
该类变量的内存均从静止区分配,生存期为整个程序的执行过程。static可以修饰局部变量也可以修饰全局变量。
static 数据类型名 变量名
由于函数内的auto局部变量在每次函数结束后都会被释放,因此不能将本次函数调用的信息保留。但是,当函数功能需要在多次调用中传递信息时,就需要一个变量能在函数结束时保留住内存而不释放,这是用到了static变量。
范例7
#include <stdio.h>
int countA(void){
static int n = 0;
++n;
return n;
}
int countB(void){
auto int n = 0;
++n;
return n;
}
int main(void){
printf("Call countA():%d\n",countA());
printf("Call countA():%d\n",countA());
printf("Call countA():%d\n",countA());
printf("\nCall countB():%d\n",countB());
printf("Call countB():%d\n",countB());
printf("Call countB():%d\n",countB());
return 0;
}
这个例子说明:auto局部变量,其内存在栈上分配,每次函数结束都会被释放。static变量为静态变量,内存从静态存储区分配,在函数结束时会将信息保留。
关键字static还可以用于声明全局变量,其作用是将该变量限制在本文件中使用。不同文件内的static全局变量可以同名,也可以与无static修饰的全局变量同名。
static局部变量的定义语句只在第一次调用时执行。
总结:
1、静态局部变量和普通局部变量不同。静态局部变量也是定义在函数内部的,静态局部变量定义时前面要加static关键字来标识,静态局部变量所在的函数在多调用多次时,只有第一次才经历变量定义和初始化,以后多次在调用时不再定义和初始化,而是维持之前上一次调用时执行后这个变量的值。本次接着来使用。
2、静态局部变量在第一次函数被调用时创造并初始化,但在函数退出时它不死亡,而是保持其值等待函数下一次被调用。下次调用时不再重新创造和初始化该变量,而是直接用上一次留下的值为基础来进行操作。
3、静态局部变量的这种特性,和全局变量非常类似。它们的相同点是都创造和初始化一次,以后调用时值保持上次的不变。不同点在于作用域不同。
4.register变量
当程序访问变量时,计算机要从内存中将变量值提取到寄存器中;运算结束后,如果该变量值发生改变,则还需把寄存器中的值重新送回到内存存放。
存储类型为register的变量的值会被要求直接存储在寄存器中。register型变量一般用来定义一些在程序中频繁使用的变量,可以提高效率。
register 数据类型名 变量名
register只能修饰函数内的变量,包括局部变量和形式参数。
void function(register int x,register int y){
register int a = 1;
}
一个变量只能声明一种存储类别。
5.extern变量
若要在变量定义前使用变量,可以借助extern来声明。
extern的作用是在一个文件中扩展全局变量的作用域。还可以使全局变量的作用域扩展到其他文件中。
例如,一个程序有两个文件file1.c和file2.c
//file1.c
int a = 1;
//file2.c
extern int a;
void main(void){
printf("a = %d\n",a);
return 0;
}
file1中已经定义了a,如果想在file2中使用该变量,则只需在开头用extern扩展a的作用域。
练习1
//使用static局部变量来完成函数的函数体,要求:返回传递给该函数的所有参数的和
#include <stdio.h>
int func(const int a){
static int sum = 0;
sum += a;
return sum;
}
int main(void){
int a = 0;
int i = 0;
int rst = 0;
printf("Please input 6 numbers:\n");
for(i=0;i<6;i++){
printf("[%d] = ",i+1);
scanf("%d",&a);
rst = func(a);
}
printf("The sum is %d.\n",rst);
return 0;
}
练习2
//使用static全局变量来完成函数的函数体,要求:返回传递给该函数的所有参数的和
#include <stdio.h>
static int sum = 0;
int func(const int a){
sum += a;
return sum;
}
int main(void){
int a = 0;
int i = 0;
int rst = 0;
printf("Please input 6 numbers:\n");
for(i=0;i<6;i++){
printf("[%d] = ",i+1);
scanf("%d",&a);
rst = func(a);
}
printf("The sum is %d.\n",rst);
return 0;
}