一个怪异C语言问题

从来没想到过,有的作者会在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。这个文章算是白写了。

发表评论

电子邮件地址不会被公开。 必填项已用*标注