文章

Increment Operator

从汇编指令层面理解自增运算

运算符后置

1
2
3
4
int func1() {
    int num = 0;
    return num++;
}

对应汇编代码:

func1():
        push    rbp                     // 将 rbp 寄存器的值压入栈中,以便在函数结束时恢复。rbp 是栈基址指针
        mov     rbp, rsp                // 将 rbp 寄存器的值设置为当前栈指针 rsp,以创建一个新的栈帧。rsp 是栈指针
        mov     DWORD PTR [rbp-4], 0    // 将常数 0 放到 [rbp-4] 表示的内存地址上。DWORD PTR 表示操作数是 32 位内存地址,[rbp-4] 即是整型变量 num 的内存地址
        mov     eax, DWORD PTR [rbp-4]  // 将 [rbp-4] 内存地址上的值存储到寄存器 eax 中
        lea     edx, [rax+1]            // 将 rax 寄存器中的值和 1 相加后,把结果存储到寄存器 edx 中。eax 是 rax 寄存器的低 32 位部分,lea 是一个 64 位指令,所以需要使用 rax 来替换 eax
        mov     DWORD PTR [rbp-4], edx  // 将 edx 寄存器中的值放到 [rbp-4] 内存地址上
        pop     rbp                     // 函数结束时,从栈中弹出 rbp 寄存器的值,恢复现场。
        ret                             // 在 x86 架构中,函数的返回值通常由 eax 提供。

可以看到 num++ 执行时,先将 num 的值存到 eax 寄存器,产生了一个 num 的临时复制,然后才对 num 递增,最后返回 num 递增之前的临时复制。

运算符前置

1
2
3
4
int func2() {
    int num = 0;
    return ++num;
}

对应汇编代码:

func2():
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], 0    // 将常数 0 放到 [rbp-4] 表示的内存地址上
        add     DWORD PTR [rbp-4], 1    // 给 [rbp-4] 内存地址上的值加 1
        mov     eax, DWORD PTR [rbp-4]  // 将 [rbp-4] 内存地址上的值存储到寄存器 eax 中
        pop     rbp
        ret

运算符前置与后置相结合

1
2
3
4
int func3() {
    int num = 0;
    return num++ + ++num;
}

对应汇编代码:

func3():
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], 0    // 将常数 0 放到 [rbp-4] 表示的内存地址上
        mov     eax, DWORD PTR [rbp-4]  // 将 [rbp-4] 内存地址上的值存储到寄存器 eax 中
        lea     edx, [rax+1]            // 将 rax 寄存器中的值和 1 相加后,把结果存储到寄存器 edx 中
        mov     DWORD PTR [rbp-4], edx  // 将 edx 寄存器中的值存到 [rbp-4] 内存地址上
        add     DWORD PTR [rbp-4], 1    // 给 [rbp-4] 内存地址上的值加 1
        mov     edx, DWORD PTR [rbp-4]  // 将 [rbp-4] 内存地址上的值存储到寄存器 edx 中
        add     eax, edx                // 将 eax 和 edx 寄存器中的值相加,结果存到 eax 中
        pop     rbp
        ret

可以从变量和表达式的角度来看待问题,num++ + ++num 由表达式 num++ 以及表达式 ++num 相加组成。依照从左到右的运算顺序,首先计算表达式 num++ 的结果,执行 num++ 后该表达式返回值 0,变量 num 值加 1 变为 1。然后计算表达式 ++num 的结果,执行 ++num 后,变量 num 值加 1 变为 2,该表达式返回值 2。最后将两个表达式返回值相加就是最终的结果。

本文由作者按照 CC BY 4.0 进行授权