免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
123下一页
最近访问板块 发新帖
查看: 15071 | 回复: 21
打印 上一主题 下一主题

[C] C语言中本地变量local variable的作用域与生存期 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-12-20 16:56 |只看该作者 |倒序浏览
《狂人C》中191页提到“每次运行到auto变量j所在block,会为j寻找存储空间,离开j所在代码模块,j的内存被释放掉。

这是不正确的。

结论应该是:对于C语言而言,本地变量会在栈开始处申请,栈销毁时结束生命。但是本地变量的作用域与所在block相关。之所以编译不通过,是因为这种block之外访问block之内变量的语法是错误的,离开本地变量所在block{},它的作用域无效,但不是说销毁了。

作用域错误是语法层面的。而生存期(存在销毁)与程序栈相关,是运行时的概念,它们是两码事。而且,有没有auto这个 keyword修饰,结果都一样,所以auto用不用没有任何意义。

关于C语言程序栈以及调用规范,可以参考我前面翻译的文章:http://sunxiunan.com/?p=1229 也可以查找wiki关于calling conversion方面的资料。

下面我们通过这段代码说明一下:

https://gist.github.com/748095

int main (int argc, char *argv[])
{

int a = 99;
int b = a;
int* pLocala = NULL;
int* pLocalb = NULL;
int d = 1;
printf("hello world \n");

do{

int a = 3;
int b = 23;
pLocala = &a;
pLocalb = &b;
b = a + 5;
printf("%d %p\n", b, &a);
}while(0);

int e = 1;
b = a;

printf("%d %p\n", b, &a);
printf("%p %p %p %p %p %p %p %p\n", &e, pLocalb, pLocala, &d, &pLocalb, &pLocala, &b, &a);
return(0);
}

如果你运行这段代码(无论是VC++还是GCC),最后printf出来的结果应该是连续的,而&pLocala 和&pLocalb其实就是一种while循环中定义的变量a和b所在地址,可以看出来它们夹在变量d与e中间。

我们可以用汇编代码看的更清楚一些,在VC2010生成的汇编代码(C源代码与前面略有不同),完整输出参考
https://gist.github.com/748116

————————————————

;    COMDAT _wmain

_TEXT    SEGMENT

_b$4410 = -32                        ; size = 4

_a$4409 = -20                        ; size = 4

_a$ = -8                        ; size = 4

_argc$ = 8                        ; size = 4

_argv$ = 12                        ; size = 4

  ; 7    : {

……

; 8    :     int a = 0;

   mov    DWORD PTR _a$[ebp], 0

$LN3@wmain:

; 9    :

; 10   :     do

; 11   :     {

; 12   :         auto int a = 3;

    mov    DWORD PTR _a$4409[ebp], 3

; 13   :         auto int b = 0;

    mov    DWORD PTR _b$4410[ebp], 0

……

——————————————

可以看到中间;12 : auto int a = 3;使用的是这样的汇编代码

mov    DWORD PTR _a$4409[ebp], 3

很显然_a$4409是在程序栈开始位置定义,而不是书中提到的”每次进入block时定义”。

http://sunxiunan.com/?p=1780

论坛徽章:
324
射手座
日期:2013-08-23 12:04:38射手座
日期:2013-08-23 16:18:12未羊
日期:2013-08-30 14:33:15水瓶座
日期:2013-09-02 16:44:31摩羯座
日期:2013-09-25 09:33:52双子座
日期:2013-09-26 12:21:10金牛座
日期:2013-10-14 09:08:49申猴
日期:2013-10-16 13:09:43子鼠
日期:2013-10-17 23:23:19射手座
日期:2013-10-18 13:00:27金牛座
日期:2013-10-18 15:47:57午马
日期:2013-10-18 21:43:38
2 [报告]
发表于 2010-12-20 17:07 |只看该作者
确实是这样的,如果是class变量,一开始的地方会调用构造,离开时析构,但栈中的空间还是在的

论坛徽章:
0
3 [报告]
发表于 2010-12-20 17:13 |只看该作者
确实是这样的,如果是class变量,一开始的地方会调用构造,离开时析构,但栈中的空间还是在的
hellioncu 发表于 2010-12-20 17:07


C语言哪来的Class?

论坛徽章:
0
4 [报告]
发表于 2010-12-20 20:41 |只看该作者
c语言中 变量的作用域是函数 c++中是离变量最近的一个block{} 吧..

论坛徽章:
0
5 [报告]
发表于 2010-12-20 20:51 |只看该作者
刚测试了下 在Lnix 下 C和C++ 中 局部变量的作用域是相同的 上边的回答有问题 抱歉!~~~

论坛徽章:
2
程序设计版块每日发帖之星
日期:2015-06-17 22:20:00每日论坛发贴之星
日期:2015-06-17 22:20:00
6 [报告]
发表于 2010-12-20 20:58 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
7 [报告]
发表于 2010-12-20 21:55 |只看该作者
本帖最后由 OwnWaterloo 于 2010-12-20 22:08 编辑

同6楼。
无论是"用汇编解释C语言", 还是用"仅用C标准解释问题", 都是片面的。

不能将某个编译器的实现当作是C语言,因为"gcc没有警告"这样的理由是站不住脚的。

例如, 从lz的汇编代码可以看出: 在lz使用的机器上, 指针是32位整数。但C语言的指针绝对不是32位整数。

那C语言指针究竟是什么?
其实绝大多数时候, 并不需要了解指针的实际实现方式, 只需要了解指针抽象出的概念就可以编程
这样既可以避免研究繁琐的汇编代码, 还可以避免无意间写出平台相关代码。


再举一例, 不能因为看到一匹马的颜色, 就说所有马都是这个颜色。
大多数时候, 只需要将马作为交通工具, 颜色并不重要。



回到书中的例子, 书中的说法(我猜测)是为了避免出现这样的代码:

  1. T* p;
  2. {
  3.       T v;
  4.       p = &v;
  5. }
  6. *p;
复制代码
除开极端情况 —— 无论编译器如何实现栈空间的分配, 无论在block结束后, 该空间是否真的被回 —— 应该认为上述代码是错误的。
因为C语言只保证automatic对象在所属block结束前有效



最后, 追根究底一下, 是否所有的局部automatic变量都在函数开始处分配?
显然不是。

举例: c99支持VLA:

  1. void f(void* p);

  2. void g(int x)
  3. {
  4.       if (x)
  5.       {
  6.             char buf[x];
  7.             f(buf);
  8.       }
  9. }
复制代码
gcc -O2 -S -masm=intel  -std=c99

_g:
        push    ebp
        mov     ebp, esp
        push    ebx
        sub     esp, 4

        mov     eax, DWORD PTR [ebp+8]
        test    eax, eax
        jne     L4

        mov     ebx, DWORD PTR [ebp-4]
        leave
        ret
        .p2align 4,,7
L4:

        add     eax, 30
        mov     ebx, esp
        and     eax, -16
        call    __alloca

        lea     eax, [esp+19]
        and     eax, -16
        mov     DWORD PTR [esp], eax
        call    _f
        mov     esp, ebx
        mov     ebx, DWORD PTR [ebp-4]
        leave
        ret

显然, buf是在进入block后分配的。


我记得该书主要关注c89。
那退回c89, 也是存在这样一种情况:

  1. #ifndef S1
  2. #define S1 1212
  3. #endif

  4. #ifndef S2
  5. #define S2 326
  6. #endif

  7. void f(void* p);

  8. void g(int x, int y)
  9. {
  10.       if (x)
  11.       {
  12.             char xx[S1];
  13.             f(xx);
  14.       }
  15.       if (y)
  16.       {
  17.             char yy[S2];
  18.             f(yy);
  19.       }
  20. }
复制代码
lz可以试试不同的S1, S2组合生成何种代码。

我列出一种情况:
cl /O1 /FAs /c /GS- /DS1=1212 /DS2=1212

得到的汇编代码:
;        COMDAT _g
_TEXT        SEGMENT
_yy$595 = -1212                                                ; size = 1212
_xx$593 = -1212                                                ; size = 1212

...

可以看出, xx和yy的空间是复用的。
也就是说, 在离开xx的block之后, 空间不再属于xx, 随后将用作yy
是否可以认为xx的空间已被回收挪为它用


总结, 排除极端情况下, 用汇编去解释C语言是费力不讨好的。
如果能在C语言的抽象层次上完成工作, 将automatic变量的空间认作 (尽管它可能是, 也可能不是) 在block结束后回收, 并不会有什么坏处, 还可以编写编译器无关代码。
即使是高手, 也应该尽可能避免写出编译器相关的代码, 更别说面向初学者的书籍。

论坛徽章:
0
8 [报告]
发表于 2010-12-20 23:08 |只看该作者
回复 7# OwnWaterloo


gcc我不熟悉,但是对于cl,发现你用了一个很有趣的优化参数O1
现在机器上没有vc环境,明天再试。

另外,我同意汇编并不等同于C,因为汇编相当于实现相关。
但是,还是觉得很多情况下,如果不看汇编,光靠描述说明,很难真正理解,
用汇编去解释部分问题,是可以加深对概念的了解。

非常感谢指出文章的不足,我会针对gcc以及vc各种变化对文章加以补充。

论坛徽章:
0
9 [报告]
发表于 2010-12-20 23:15 |只看该作者
谢谢几位网友发表见解
事实上
auto类别的局部变量的含义是在用到这个局部变量时,“自动”地为它找个存储空间,离开它的作用范围后,它所占据的空间自动地还给计算机。这里所谓的不再用到是指,这个局部变量所在的代码模块已经被执行完了。当程序再次开始执行auto类别的局部变量所在的代码模块中的代码时,会再次为这个变量寻找一个存储空间,基本上这个空间和上一次它所占据的空间是不一样的,即使一样也毫无意义,因为原来所占据的内存空间既然还给了操作系统,那么里面的内容——原来的值,可能早就面目全非了。

的文字和表达上确实有问题
我考虑换种表达方法

希望几位网友给些建议

论坛徽章:
0
10 [报告]
发表于 2010-12-20 23:23 |只看该作者
我刚刚想到了这样一个有点极端的例子

  1. {
  2. auto type v;
  3. ……
  4. goto L1 ;
  5. ……
  6. L2 :……
  7. ……
  8. }
  9. L1 :……
  10. ……
  11. goto L2 ;
  12. ……
复制代码


我那段文字似乎很难解释这个
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP