C语言陷阱与技巧第1节,C语言函数中的局部变量作用范围是整个函数吗?

在开发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语言函数中的局部变量的作用范围有时并不是整个函数,利用这一点可以在一定程度上解决变量名被占用的问题。不过应该注意,函数内部子{}代码块里的局部变量占用的内存也在函数执行完毕之后才会被释放。

阅读更多:   C语言
添加新评论

icon_redface.gificon_idea.gificon_cool.gif2016kuk.gificon_mrgreen.gif2016shuai.gif2016tp.gif2016db.gif2016ch.gificon_razz.gif2016zj.gificon_sad.gificon_cry.gif2016zhh.gificon_question.gif2016jk.gif2016bs.gificon_lol.gif2016qiao.gificon_surprised.gif2016fendou.gif2016ll.gif