一段有意思的C语代码分析
某博客里看到这么一段代码,用多个编译器编译出来都无法确定结果,因为结果不一样,代码如下:
void main() { int i = 1; printf("%d,%d,%d",i++,++i,i); printf("\n\n"); }
很普通的代码,i++ 和 ++i是一样的,都是自动加1,我分别用了 VC、TC 和 GNU/Linux 下的 GCC 编译,结果如下:
VC debug 方式编译,结果为2,2,1;Release 方式编译,结果为2,3,3
GCC 编译结果为2,3,3
TC 编译结果为2,2,1
可以看到结果不一样的,于是,我把 VC 分别用 release 和 debug 方式编译的和 TC 编译的反汇编了,GNU/Linux 下 GCC 的就懒得管了,代码贴上来,不过这里说明一点,C 入栈是从右到左的顺序。
VC 下用 Debug 方式编译的反汇编:
.text:00401028 mov [ebp+var_4], 1 .text:0040102F mov eax, [ebp+var_4] ;eax=1 .text:00401032 push eax .text:00401033 mov ecx, [ebp+var_4] ;ecx=1 .text:00401036 add ecx, 1 ;ecx 加 1,ecx=2 .text:00401039 mov [ebp+var_4], ecx ;[ebp+var_4] 现在为 2 .text:0040103C mov edx, [ebp+var_4] ;edx=2 .text:0040103F push edx .text:00401040 mov eax, [ebp+var_4] .text:00401043 mov [ebp+var_8], eax .text:00401046 mov ecx, [ebp+var_8] .text:00401049 push ecx .text:0040104A push offset aDDD ; "%d,%d,%d" .text:0040104F mov edx, [ebp+var_4] .text:00401052 add edx, 1 .text:00401055 mov [ebp+var_4], edx .text:00401058 call printf .text:0040105D add esp, 10h .text:00401060 push offset asc_42201C ; "\n\n" .text:00401065 call printf .text:0040106A add esp, 4 .text:0040106D pop edi .text:0040106E pop esi .text:0040106F pop ebx .text:00401070 add esp, 48h .text:00401073 cmp ebp, esp .text:00401075 call __chkesp .text:0040107A mov esp, ebp .text:0040107C pop ebp .text:0040107D retn .text:0040107D main endp
VC 下用 Release 方式编译后的反汇编:
.text:00401000 push 3 .text:00401002 push 3 .text:00401004 push 2 .text:00401006 push offset aDDD ; "%d,%d,%d" .text:0040100B call sub_401020 .text:00401010 push offset asc_407030 ; "\n\n" .text:00401015 call sub_401020 .text:0040101A add esp, 14h .text:0040101D retn .text:0040101D _main endp
TC 编译后的反汇编:
seg000:01FA push bp seg000:01FB mov bp, sp ;sp 和 bp平等 seg000:01FD push si seg000:01FE mov si, 1 ;si=1 seg000:0201 push si seg000:0202 inc si ;自动加 1 seg000:0203 mov ax, si ;ax=2 seg000:0205 push ax seg000:0206 mov ax, si seg000:0208 inc si ;自动加 1 seg000:0209 push ax ;ax=2 seg000:020A mov ax, 194h seg000:020D push ax ; format seg000:020E call _printf seg000:0211 add sp, 8 seg000:0214 mov ax, 19Dh seg000:0217 push ax ; format seg000:0218 call _printf seg000:021B pop cx seg000:021C pop si seg000:021D pop bp seg000:021E retn seg000:021E _main endp
Release 方式编译,代码会经过优化的,在编译生成时,计算出了三个 i 的值,从上面 Release 方式编译后的反汇编代码中可以看出,三个 i 的值是直接将计算好的压入栈。编译出了逻辑问题了?编译时i成了一个累加器了,它首先给 i 赋值,i=1,此时 i 的值为1,当遇到第一个 i++ 时,i=2 了,++i 时,i 又自动加 1,i=3,最后一个i就是相加后的 i,i=3,所以它编译的输出结果是 2,3,3。
i 值应该是不变化的,也就是 int i=1,i 就等于1,当遇到第一个 i++ 时,i=2,当遇到第二个 ++i 时,i=2,最后一个 i,i 应该是 i=1,i 的值不会随着数位相加而值变化,最后结果应该是 2,2,1 才对,欢迎发表评论讨论。