免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: sagasw
打印 上一主题 下一主题

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

论坛徽章:
0
11 [报告]
发表于 2010-12-21 09:00 |只看该作者
回复 7# OwnWaterloo

果然如我所料,如果没有O1这个编译选项,VC++2010是不会压缩空间的。
如果加了O1这个"mini space"优化选项,出来的结果也不是OwnWaterloo提到的,临时变量都没有了,优化得很彻底。
我用的是VC2010,过早的优化果然罪恶!(开个玩笑)

无论如何,还是要谢谢OwnWaterloo的说法:勿以汇编释语言。
其实我也同意这个看法,所以尽量以K&R来作为解读文本。
但是对于一些实现相关的内容,如果还有通过自身的解释或者对标准的注解来做说明,那是非常费劲的。
《狂人C》中对++前后缀方式的说明就是如此,尽管键盘农夫试图把++解释的更为通俗易懂深入浅出,但是我看了还是一头雾水。
而当我看过++代码生成的“无优化VC++2010特别为了注释”版本以后,就恍然大悟,源码之下别无秘密。
而且,如果没有我用汇编展示的这个例子,OwnWaterloo大侠也不会出手解释,从这点来讲,也是抛砖引玉。

当然也可以说我这是一种误读,太细化,系统及平台依赖太强,
但是我感觉用汇编来解读部分C语言实现相关的细节,在现阶段对我而言是有帮助的,那就可以了。

int _tmain(int argc, _TCHAR* argv[])
{
        int a = 0;
        do
        {
                char buf[1024];
                buf[0] = 1;
        }while (0);

        a = 3;

        do
        {
                char buf[1024];
                buf[0] = 93;
        }while(0);

        return 0;
}

ASM输出结果为:

_TEXT        SEGMENT
_buf$4413 = -2076                                        ; size = 1024
_buf$4409 = -1044                                        ; size = 1024
_a$ = -12                                                ; size = 4
__$ArrayPad$ = -4                                        ; size = 4
_argc$ = 8                                                ; size = 4
_argv$ = 12                                                ; size = 4
_wmain        PROC                                                ; COMDAT

; 7    : {

        push        ebp
        mov        ebp, esp
        sub        esp, 2272                                ; 000008e0H
        push        ebx
        push        esi
        push        edi
        lea        edi, DWORD PTR [ebp-2272]
        mov        ecx, 568                                ; 00000238H
        mov        eax, -858993460                                ; ccccccccH
        rep stosd
        mov        eax, DWORD PTR ___security_cookie
        xor        eax, ebp
        mov        DWORD PTR __$ArrayPad$[ebp], eax

; 8    :         int a = 0;

        mov        DWORD PTR _a$[ebp], 0
$LN6@wmain:

; 9    :
; 10   :         do
; 11   :         {
; 12   :                 char buf[1024];
; 13   :                 buf[0] = 1;

        mov        BYTE PTR _buf$4409[ebp], 1

; 14   :         }while (0);

        xor        eax, eax
        jne        SHORT $LN6@wmain

; 15   :
; 16   :         a = 3;

        mov        DWORD PTR _a$[ebp], 3
$LN3@wmain:

; 17   :
; 18   :         do
; 19   :         {
; 20   :                 char buf[1024];
; 21   :                 buf[0] = 93;

        mov        BYTE PTR _buf$4413[ebp], 93                ; 0000005dH

; 22   :         }while(0);

论坛徽章:
0
12 [报告]
发表于 2010-12-21 10:00 |只看该作者
回复  OwnWaterloo
《狂人C》中对++前后缀方式的说明就是如此,尽管键盘农夫试图把++解释的更为通俗易懂深入浅出,但是我看了还是一头雾水。
sagasw 发表于 2010-12-21 09:00


sagasw网友提到的++部分
http://bbs.chinaunix.net/thread-1830222-17-1.html
敬请各位方家指正

论坛徽章:
0
13 [报告]
发表于 2010-12-21 10:23 |只看该作者
在这里copy一下,原帖也做了回复:

    我对这段解释的看法是,为了解释++,键盘农夫不得不在一开始就引入更为难懂的概念“左值”“右值”(我对书中这个概念几乎都是跳过),
第一点,C语言中(至少是在K&R中)我没有看到右值的提法,RValue其实是从C++跑来的。
我在今年五月份还试图写博客来解释这个问题,http://sunxiunan.com/?p=1628 除了堆砌一些专家的提法,感觉解释的很不成功。

    用汇编语言解释是很讨巧的一种方式,但是作为教科书可能没法使用,因为不可移植以及太强的平台、软件依赖。

    但是我也不赞同使用用另一个更复杂的概念来解释,这只会让读者更浆糊。如果两者选择,我倾向于使用汇编。

    前缀型++和后缀型++相对在C语言中的应用并不广泛,如果不在表达式中使用,它们基本上是一样的。而我个人也不建议++混在在其他表达式中,比如函数调用作为参数传入也好,作为赋值表达式出现也罢,都有极强的破坏性和副作用,最好的规则就是,++只要单独运行,无论前后。

论坛徽章:
0
14 [报告]
发表于 2010-12-21 11:54 |只看该作者
在这里copy一下,原帖也做了回复:

    我在今年五月份还试图写博客来解释这个问题,http://sunxiunan.com/?p=1628 除了堆砌一些专家的提法,感觉解释的很不成功。
sagasw 发表于 2010-12-21 10:23

那个帖子我仔细看过
(怯怯地表示)
许多专家的提法
我感到也很难苟同

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
15 [报告]
发表于 2010-12-21 11:58 |只看该作者
回复 8# sagasw

—— 关于gcc

其实我也是从vc开始学的…… 至今都不习惯at&t的汇编格式。
但我这里支持c99的编译器就只有它了……


—— 关于O1

其实, 对这种"无法证明, 但只需要一个反例即可证伪" 的命题, 明智的做法是避实就虚。
只要能做到不依赖这种命题的真伪也能编写出正确高效的代码, 那命题的证明可以先抛到一边, 编程才是最终目的。

只是呢, 在CU上, 如果仅纯理论的分析, 不拿出点实际的东西(比如, 让新手侧目、 让高手认可的汇编代码) 可能会让人认为仅仅是在"耍嘴皮子"而已。
所以我必须费点力气找几个反例出来。
既然是找反例, 自然也就不择手段了……



—— 关于汇编

仅从C语言的语法语意去理解确实有些抽象。
需要一些例子( C语言的小程序, 或者汇编代码)来帮助理解。

但需要注意的是, 小程序的结果、 产生的汇编代码, 是C语言的实现, 甚至可能是若干种可能实现的其中一种。
它们是用来协助理解C语言, 但不是C语言的唯一解释。
最好能摸清这些结果、这些汇编代码中哪些行为是C语言有保证的, 并尽可能利用这些部分来编程。

这可以避免很多类似: "我的程序在linux/ gcc/ 未优化/ gcc低版本/ 去掉free 下可以工作, 怎么在windows/ vc/ 高优化/ gcc高版本/ 添加free就崩了啊" 的问题。
编程时依赖的细节越少, 当这些细节发生变化时受到的影响也越少。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
16 [报告]
发表于 2010-12-21 12:03 |只看该作者
回复 11# sagasw

这段代码给编译器的上下文太多了。

人肉就能分析出它等效于 int main(void) { return 0; }
而对一些死板的优化工作, 编译器会做得更好。
vc高优化下, 这代码主体应该就只有 xor eax eax。


7楼的c89的代码中, 让x和y作为参数是为了避免编译器得知它们的值, 让block是否进入是未知的。
而f函数只声明不定义, 编译器不知道它究竟会做什么, 就不能随意省略两个数组。
在 (vc6, vc8, vc9, vc10)×(O1, O2, Ox)的组合下都复用了空间。
在(vc6, vc8, vc9, vc10)×(Od)的组合下都没有复用空间。

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

关于前缀、 后缀

为神马需要汇编啊……
为神马需要左值右值啊……
我记得学到这里的时候, 很容易理解啊……

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

回复 10# KBTiller

goto, 这么不和谐……

貌似解释很容易……  貌似, 在C语言里, 进入一个block的方式不仅仅是从第1个语句开始……

  1. void f(void* p);

  2. void g(int x, int y, int z)
  3. {
  4.       if (x)
  5.       {
  6.             char buf[1212] = {0};
  7.             enter:
  8.             f(buf);
  9.       }

  10.       if (y)
  11.       {
  12.             y = 0;
  13.             goto enter;
  14.       }

  15.       switch (z)
  16.       {
  17.             char buf[326] = {0};
  18.       case 0:
  19.             f(buf+19);
  20.             break;
  21.       case 1:
  22.             f(buf+86);
  23.             break;
  24.       }
  25. }
复制代码
从第2个if跳到第1个if中, 从switch进入两个case中, 貌似都算进入一个block。
只是它们只有空间, 没有机会得到初始化了 ( 若x为真, 执行第1个if时是会得到初始化的)。

而在C++中, 这种跳过初始化的局部跳转语句就非法了。


还有一个更邪恶的setjmp/ longjmp, 它们的使用比goto/ switch有更多限制, 应该也能解释。


平时用得不多, 拿不准, 期待专家考察。

论坛徽章:
0
19 [报告]
发表于 2010-12-21 12:23 |只看该作者
本帖最后由 KBTiller 于 2010-12-21 12:33 编辑

回复 18# OwnWaterloo


    我懊悔的是用了“离开”这个词
    下面的东西无法自圆其说


{
  ……
  function();
  ……
}

论坛徽章:
0
20 [报告]
发表于 2010-12-21 12:34 |只看该作者
回复  KBTiller
而在C++中, 这种跳过初始化的局部跳转语句就非法了。
OwnWaterloo 发表于 2010-12-21 12:16

C++有不少东西都比较好
函数原型我觉得也很好
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP