从来没想到过,有的作者会在C语言的教材提出这样的问题,而且有的学生会看这样的教材。总之是个++i和i++的一个复杂组合问题,如果你没遇到这个问题,你还是不要看了,如果你遇到这个问题,说明你自己有问题(学习方法的问题)。问题如下:
int main(){ int m=6; int q=(++m)+(++m)+(++m); printf("q=%d\n",q); return 0; } //q的值为什么是25?
我有点想拍人的冲动了,怎么分析都不合理,只得反汇编。我用的是linux系统,用gcc即可完成c到汇编的这个编译过程(gcc -s),如下是main函数对应的反汇编代码:
main: .LFB2: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp movl $6, -8(%rbp) #看到没,在这里把6赋值给了m addl $1, -8(%rbp) #然后给m加了1 addl $1, -8(%rbp) #然后又给m加了1,为什么是又?此时m=8 movl -8(%rbp), %eax #此时把8复制到了eax寄存器,eax和rax可以等同 leal (%rax,%rax), %edx #这个其实就是把rax的值相加,并给了edx,edx=16 addl $1, -8(%rbp) #有对m加了1,此时m=9 movl -8(%rbp), %eax #然后把9复制给eax,eax=9 addl %edx, %eax #把eax和edx相加,结果放eax,eax=25 movl %eax, -4(%rbp) movl -4(%rbp), %eax movl %eax, %esi movl $.LC0, %edi movl $0, %eax call printf movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc
汇编代码如上,我加了点注释,才发现很多东西和理解的不是很一样。在此特别说明“-8(%rbp)”是变量m在栈中的访问方式,其实对应的就是变量m,“eax”、“rax”其实都是一个寄存器,只不过一个是32位,一个是64位的。
分析后,我恍然大悟了:结果描述如下:
初始条件:m=6 q=(++m)+(++m)+(++m) 如下描述执行结果,0-5是执行步骤和个步骤的m值 0.m=6 q=(++6)+(++6)+(++6) #我们用6替换了变量m 1.m=7 q=(++7)+(++7)+(7) #看到了吧,左边数第一个括号的++导致了所有m变成了7 2.m=8 q=(++8)+(8)+(8) #左边数第二个括号导致m变成了8 3.m=8 q=(++8)+(16) #左边两个变量完成了加法运算 4.m=9 q=(16)+(9) #此时右边数第一个括号的++导致m变成了9 5.m=9 q=25 注:在第4步时,m的++操作已经不影响16了,因为16已经被计算出来了
虽然已经弄明白了,但是这种问题真的不值得去研究。
说明:两天后又在Visual Studio上用C++编译了一下,结果竟然是27。这个文章算是白写了。