在开发C语言程序的过程中,若是能为变量取一个合适的名字,即使不写额外的注释也能让整个程序段一目了然。不过遗憾的是,有时候好用的名字会被其他变量占用,这时为变量取名就是一件头疼的事了。
我常常在同事的代码里看到诸如 size1, size2, size3 的变量名,令人眼花缭乱。
局部变量的作用域,是整个函数吗?
总体来说,C语言中的变量可根据作用域的不同分为全局变量和局部变量。一般来说,定义在函数中的变量被称为局部变量,非 static 局部变量存在于函数的栈帧中,函数的栈帧在其执行完毕后会被系统回收,此时局部变量自动释放,因此局部变量只在函数中有效。这一点相信即使C语言初学者,也是明白的。
不过不知道读者有没有想过这个问题:C语言函数中的局部变量作用范围是整个函数吗?比如,在函数 fun() 中定义了变量 size,那 size 是否在 fun() 函数所属的整个代码段都可用呢?在回答这个问题之前,应明白,至少gcc version 4.8.4 已经不再限制C语言函数定义变量必须在代码段的开头了,也就是说下面这段C语言代码是合法的:
void fun()
{
printf("something\n");
int i = 3;
printf("i=%d\n", i);
}
定义变量 i 可以在若干行语句之后,甚至像下面的C语言代码这样,在 if 语句块中定义变量 size 也是合法的,请看:
int main()
{
int i = 3, j;
j = i++;
if(j > 3){
int size = 30;
}
return 0;
}
现在在上面的C语言 main() 函数返回前添加一行 size ++;,即:
编译并执行添加 size++; 之后的C语言代码,会发生什么呢?请看:
# gcc tmp.c
tmp.c: In function ‘main’:
tmp.c:8:2: error: ‘size’ undeclared (first use in this function)
size ++;
^
编译失败了,提示第 8 行的 size 没有声明!这就奇怪了,size 明明在上面的C语言代码第 6 行声明了啊?!这其实就解答了“C语言函数中的局部变量作用范围是整个函数吗?”这个问题,显然,局部变量 size 的作用范围就不是整个函数。
不知道会不会有朋友认为:编译失败是因为 if 语句没有执行,所以 int size = 30; 也没有执行,因此 size 没有声明。
既然局部变量作用域不是整个函数,那它的作用域是什么呢?
相信读者已经知道,C语言代码中具有人类语言意义的 main、if、while 等词只是为了便于程序员使用,计算机并不能识别这些单词,因此若想执行C语言程序,需要通过编译器将代码“翻译”成计算机认识的指令序列。
编译器在编译C语言代码过程中,也要处理变量的作用范围问题。应明白“全局”和“局部”是对相对概念,所以编译器首先需要定义一个“局部”的标准,将“{}”符号包围的代码视为一个“局部”就是一个不错的选择。
以上分析基于 gcc version 4.8.4。
现在就清楚了,局部变量的作用范围就是它所属的 "{}" 包围的代码块,而全局变量的作用范围则是多个 "{}" 包围的代码块。再来看上面的问题,main() 函数中第 6 行的 size 属于 if{} 代码块,而第 8 行的 size 属于 main{} 代码块,因此虽然它俩名字一模一样,但是编译器仍然会认为是不同的变量,编译就报错了。
事实上,在C语言程序开发中,可以只使用“{}”做局部处理,例如下面这段代码:
#include <stdio.h>
int main()
{
int size = 12;
{
int size = 8;
size ++;
printf("inside size: %d\n", size);
}
printf("outside size: %d\n", size);
return 0;
}
编译上面这段C语言代码并执行,结果如下:
# gcc t.c
# ./a.out
inside size: 9
outside size: 12
显然,函数内部的 "{}" 包裹的C语言代码段并没有影响到外面的 size,这下就不用怕变量名被占用了。
函数内部子{}代码块运行完毕之后,变量内存会被释放吗?
这个问题的答案其实上面已经解释了,变量即使是函数内部子{}代码块里的局部变量,它也是存储在函数的栈帧内的,而栈帧只有在整个函数运行完毕后才会被系统回收,所以函数内部子{}代码块里的局部变量占用的内存也在函数执行完毕之后才会被释放。
可以做如下实验,首先在函数的子 {} 代码块外部定义一个很大的数组,相关C语言代码如下,请看:
int main()
{
char size[2*1024*1024] = {};
{
}
while(1);
return 0;
}
编译并执行这段C语言代码,然后使用 top 命令查看程序所占资源如下:
然后,在函数的子 {} 代码块内部定义一个很大的数组,相关C语言代码如下,请看:
int main()
{
{
char size[2*1024*1024] = {};
}
while(1);
return 0;
}
编译并执行这段C语言代码,然后使用 top 命令查看程序所占资源如下:
已经一目了然了。
小结
C语言函数中的局部变量的作用范围有时并不是整个函数,利用这一点可以在一定程度上解决变量名被占用的问题。不过应该注意,函数内部子{}代码块里的局部变量占用的内存也在函数执行完毕之后才会被释放。