三分钟弄清楚C语言为何函数退出就不能使用局部变量了,不初始化局部变量会出错吗


上一次,我们说到了全局变量和局部变量,并且介绍了局部变量在函数返回后,就会被释放。而全局变量却可以一直保存到程序结束,这是为什么呢?在回答这个问题之前,请看下面这张非常经典的图:
这个图就是程序在运行所需的内存布局。简单来说,就是程序在运行时会占用内存,占用的内存每个区域用途都是不同的,有的区域用做堆区,有的用做栈区,等等。

为什么函数返回时,局部变量就不能用了

程序每调用一个函数,系统就自动在栈区划分一块区域给该函数使用,函数内部定义的局部变量,也存在此处。因为并不能知道系统分配的栈区原来填充的是什么样的数据,所以如果函数内部定义的局部变量没有初始化(没有赋初值)就使用它,它的值也是未知的。

当函数执行完毕,返回时,系统将收回这块分配的栈区,所以函数的局部变量的值就不能继续使用了。

说了这么多空的,我们来看一个例子,下面的代码非常简单,就是在 test 函数中定一个了一个局部 int 型变量 i,然后打印出它的值,再赋值为 321,然后在 main 函数中调用 它两次。

#include <stdio.h>
int test(void)
{
    int i;
    printf("%d\n", i);
    i = 321;
}
int main(void)
{
    test();
    test();
    return 0;
}


在编译执行前,根据我们的理论,局部变量如果没有初始化,那它的值是未知的,因此第一个 test 调用,会打印出随机的一个数。虽然第一个 test 函数在最后对局部变量 i 赋值了 321,但是在它返回后,就被释放了。因此第二次调用 test 时,输出应该还是一个随机数。那对不对呢?编译执行,发现程序输出

134567128
321

出乎意料,第二次打印出的值正是第一个 test 末尾赋的值 321。有一种初学者是这样,原本就没有把这条语法规则记牢,或者对自己的记忆力没信心,看到这个结果就会想:哦那肯定是我记错了,改过来记吧,应该是“函数中的局部变量具有一直存在的固定的存储空间,每次函数调用时使用它,返回时也不释放,再次调用函数时它应该还能保持上次的值”。

还有一种初学者是怀疑论者或不可知论者,看到这个结果就会想:教材上明明说“局部变量的存储空间在每次函数调用时分配,在函数返回时释放”,那一定是教材写错了,教材也是人写的,是人写的就难免出错,哦,连C99也这么写的啊,C99也是人写的,也难免出错,或者C99也许没错,但是反正运行结果就是错了,计算机这东西真靠不住,太容易受电磁干扰和宇宙射线影响了,我的程序写得再正确也有可能被干扰得不能正确运行。

这两种想法当然不对。我们说,“因为并不能知道系统分配的栈区原来填充的是什么样的数据,所以如果函数内部定义的局部变量没有初始化(没有赋初值)就使用它,它的值也是未知的。”,这个未知的值可以是随机的,那既然是随机值,它为什么不可以恰好是 321 呢?

函数返回后,系统收回原分配的栈区,却不一定会去清零它。如果下一次,恰好又把这块栈区分配给 test 函数,局部变量 i 所在的区域恰好是上一次 i 所在的区域,而上次 test 函数返回之前,把这块区域赋值为 321 了,那这次 i 的“未知值”就正好是 321 也就不奇怪了,对吧。

为了验证我们上面的解答,我们在两个 test 之间加一行 printf,再做次实验:

...
int main(void)
{
    test();
    printf("something\n");
    test();
    return 0;
}

再编译执行,发现输出为

134567128
something
0

看到没,第一个 test 函数末尾赋的值,并不能保证一定传给下一次 test。

未初始化的局部变量的值是不确定的,上一次谁使用过这一块的栈区,恰好留在局部变量所在区域的值等于几,这次局部变量的未初始化的默认值就是几。

全局变量为什么可以一直保留到程序结束?

这是因为,全局变量要么保存在 bss 段,要么保存在 data 段,这两个段会一直保留到程序结束。

未初始化的全局变量保存在 bss 段,已初始化的全局变量保存在 data 段。
阅读更多:   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