一、作用域
1、作用域问题:在一个函数中定义的变量,在其他函数中能否被引用?在不同位置定义的变量,在什么范围内有效?
2、定义变量可能有3种情况 [谭浩强]
(1)在函数的开头定义;
(2)在函数内的复合语句内定义;
(3)在函数的外部定义。
3、局部变量
(1)在函数内部定义的变量,只有在本函数内才能引用它们,也只有在本函数范围内有效;
(2)在复合语句内定义的变量,只有在本复合语句内才能引用它们,也只有在本复合语句范围内有效;
(3)举例如下,两个函数中的tmp只作用域在自己的函数体内。
//
#include
void cpri()
{
char tmp= '6';
printf("cpri: %c\n", tmp);
}
void ipri()
{
int tmp = 666;
printf("ipri: %d\n", tmp);
}
int main()
{
cpri();
ipri();
return 0;
}
//
//
运行结果
cpri: 6
ipri: 666
//
4、全局变量
(1)在函数之外定义的变量是全局变量。
(2)C程序设计中,一般习惯将全局变量名的首字母大写。
(3)尽力减少使用全局变量的原因
a、内存开销大,全局变量在程序整个执行过程中都占有存储单元;
b、降低函数的通用性,不利于函数作为一个功能模块拷贝到别的文件中复用;
c、代码的可阅读性降低,人们难以清楚的判断出每个瞬时,各个外部变量的值。
二、变量的存储方式和生命周期
1、变量的存储方式有两种
(1)静态存储方式
A、静态存储方式是指在程序运行期间由系统分配固定的存储空间的方式。
B、全局变量全部存放在静态存储区中,在程序开始执行时给全局变量分配存储区,程序执行完毕就释放。整个执行过程中它们占据着固定的存储单元。
(2)动态存储方式
A、动态存储方式是指在程序运行的期间根据需求进行动态的分配存储空间的方式。
B、动态存储区中存放以下数据:
① 函数形式参数。在调用函数时给形参分配存储空间。
② 函数中定义的没有用关键字static声明的变量。即自动变量。
③ 函数调用时的现场保护和返回地址等。
注意:上述的分配和释放时动态的,如果一个程序中两次调用同一函数,而在此函数种定义了局部变量,在2次调用时分配该局部变量获得的存储空间的地址可能是不同的。
2、变量的存储类别
(1)C语言中,每一个变量和函数都有2个属性:数据类型和数据的存储类别。C的存储类别有4种:自动的(auto)、静态的(static)、寄存器的(register)、外部的(extern)。变量的存储类别对应变量的作用域与生命周期。
3、局部变量的存储类别
(1)自动变量(auto变量)
函数中的局部变量,如果不专门声明是static存储类别,都是动态地分配存储空间的。函数中的形参和在函数中定义的局部变量(包括符合语句中的局部变量)都属于此类。如 函数中定义变量 int a; 和 auto int a; 是等价的,关键字“auto”是默认省略的。
(2)静态局部变量(static局部变量)
有时希望函数中的局部变量的值在函数调用结束后不消失,继续保留原值,即其占用的存储单元不释放,在下一次再调用该函数时,该变量已有值。此时应该用static声明该变量为“静态局部变量”。
说明:
A、静态局部变量属于静态存储类别,在静态存储区内分配存储单元。自动变量(即动态局部变量)属于动态存储类别,分配在动态存储空间中。
B、静态局部变量实在编译时赋初始值的,即只赋值一次。
C、如果在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值0(对数值型变量)或空字符‘\0’(对字符变量)。而对自动变量来说,它的值是一个不确定的值。这是由于每次函数调用结束后存储单元已经释放,下次调用时又重新另分配存储单元,所分配的单元中的内容时不可知的。
D、虽然静态局部变量在函数调用结束后仍然存在,但别的函数不能引用它。因为它是局部变量。
(3)寄存器变量(register变量)
A、如果一个变量频繁使用,可以声明为寄存器变量。由于寄存器的读写速度远快于内存的读写速度。所以能提高执行效率。
B、由于现在的计算机的速度愈来愈快,性能愈来愈高,优化的编译系统能够识别出使用频繁的变量,从而自动地将这些变量放在寄存器中,而不需要程序设计者指定。因此,现在实际上用register声明变量的必要性不大。
4、全局变量的存储类别
全局变量都是存放在静态存储区中的。
(1)在一个文件内扩展外部变量的作用域,应在引用前用关键字extern对该变量作“外部变量声明”,表示把该外部变量的作用域扩展到此位置。
//
#include
int test()
{
extern num;
printf("The num is : %d\n", num);
return 0;
}
int num = 666;
int main()
{
test();
return 0;
}
//运行的结果时 The num is : 666
注意:用extern声明外部变量时,类型名可写可不写,如"extern int num;"也可以写成"extern num;"。因为它不是定义变量,可以不指定类型,只许写出外部变量名即可。
(2)将外部变量的作用域扩展到其他文件
A、如在file1.c中定义 int num; 在file2.c中 加上#include "file1.c" ,然后在需要引用的地方前面加上 extern int num; 即可。
注:extern即可以用来扩展外部变量在本文件中的作用域,又可以使外部变量的作用域从一个文件扩展到程序中的其他文件。编译器区别处理原则是:遇到extern时,现在本文件中找外部变量的定义,如果找到,就在本文件中扩展作用域;如果找不到,就在连接时从其他文件中找外部变量的定义。如果找到就将作用域扩展到本文件;如果没有就报错。
(3)将外部变量的作用域限制在本文件中
A、有时在程序设计中希望某些外部变量只限制被本文件引用,而不被其他文件引用,这时可以在定义外部变量时加一个static声明。
B、加上 static 声明、只能用于本文件的外部变量称为静态外部变量。
注
1、for中初始化语句定义变量
#include
int main()
{
for (int i = 0; i < 3; i++) {
printf("Hello\n");
}
/* printf("%p\n", &i); */
for (int i = 0; i < 3; i++) {
printf("World\n");
}
return 0;
}
代码中两和for 循环初始化语句定义的变量一样,运行没有问题,新版本语法规范规定,for循环中,局部变量内存的分配在循环开始时,释放在循环结束时。
致谢
1、《C语言程序设计》[第四版],作者谭浩强
2、C语言 变量的作用域和生命周期