a=a++?

有这样一段代码:

int a = 2;
a = a++;
printf("%d\n", a);

在Visual Studio2012编译环境里,输出的结果是3。而用gcc编译得到2。
这是为什么?

先说GCC。出现2的原因是,gcc在做右边操作数为a++的赋值操作时,会将a的原值和++后的新值放在两个寄存器内,然后按先给a新值、后给左边操作数以原值的顺序来执行代码。用c语言来说,就是先++,再赋值。可以参照b=a++的编译结果:

    movl    $2, -8(%rbp)    // a = 2
    movl    -8(%rbp), %eax    // 原值2
    leal    1(%rax), %edx    // 新值3, a++
    movl    %edx, -8(%rbp)    // a = 3 先赋新值给回a地址
    movl    %eax, -4(%rbp)    // 再赋原值2给内存b地址

而在a=a++时,还按照这种顺序来就会出现奇怪(?)的现象。

    movl    $2, -4(%rbp)       // a = 2
    movl    -4(%rbp), %eax      // 原值
    leal    1(%rax), %edx       // 新值
    movl    %edx, -4(%rbp)      // 先赋新值3
    movl    %eax, -4(%rbp)      // 再赋原值2,又改回去了

而VS编译器的做法也应该能猜到了,先给回原值,再给新的值。

    mov eax, DWORD PTR _a$[ebp]   ;a=-8
    mov DWORD PTR _a$[ebp], eax   ;先给原值
    mov ecx, DWORD PTR _a$[ebp]
    add ecx, 1
    mov DWORD PTR _a$[ebp], ecx   ;再给新值

但是,千万别觉得VS比GCC高到哪里去。原因是这条语句本身就不被c标准支持,所以各个厂家按照自己编译器标准来,以至于出来两种截然不同的“标准”。毕竟,写c程序也要按照基本法啊。


后记:出于好奇,我在ubuntu14.04下用最新的arm-linux-gnueabi-gcc交叉编译了一下同样的程序:

#include <stdio.h>

int main()
{
    int a = 2;
    int b = 3;
    a = a ++;
    return 0;
}

arm汇编代码如下:

    mov r3, #2
    str r3, [fp, #-12]
    mov r3, #3
    str r3, [fp, #-8]
    ldr r3, [fp, #-12]
    add r3, r3, #1
    str r3, [fp, #-12]
    mov r3, #0
    mov r0, r3

全程只用一个寄存器r3,并且直接在加完1后就赋值回去了!
对比一下b=a++的代码,明显可以看到其中的奥妙:编译器直接忽略了a=a的两句指令。

    mov r3, #2
    str r3, [fp, #-12]   ; a = 2
    mov r3, #3    
    str r3, [fp, #-8]    ; b = 3
    ldr r3, [fp, #-12]   
    str r3, [fp, #-8]    ; b = a
    ldr r3, [fp, #-12]
    add r3, r3, #1       
    str r3, [fp, #-12]   ; a ++
    mov r3, #0
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值