【问题标题】:Difference between x++ and ++x in compiler implementation编译器实现中x++和++x的区别
【发布时间】:2012-10-06 16:08:00
【问题描述】:

我搜索了x++ vs ++x,找到了一个很好的答案here,所以我决定看看gcc的汇编输出,看看x++和++x是如何实现的:

main() { int s = 0; ++s;返回0; }

编译示例:

gcc mul.c -masm=intel -o mul.asm

++s的输出:

    .file   "mul.c"
    .intel_syntax
    .text
    .p2align 4,,15
.globl main
    .type   main, @function
main:
    lea %ecx, [%esp+4]
    and %esp, -16
    push    DWORD PTR [%ecx-4]
    push    %ebp
    mov %ebp, %esp
    push    %ecx
    sub %esp, 16
    mov DWORD PTR [%ebp-8], 0
    add DWORD PTR [%ebp-8], 1
    mov %eax, 0
    add %esp, 16
    pop %ecx
    pop %ebp
    lea %esp, [%ecx-4]
    ret
    .size   main, .-main
    .ident  "GCC: (GNU) 4.2.1 20070719  [FreeBSD]"      

x++ 的输出:

    .file   "mul.c"
    .intel_syntax
    .text
    .p2align 4,,15
.globl main
    .type   main, @function
main:
    lea %ecx, [%esp+4]
    and %esp, -16
    push    DWORD PTR [%ecx-4]
    push    %ebp
    mov %ebp, %esp
    push    %ecx
    sub %esp, 16
    mov DWORD PTR [%ebp-8], 0
    add DWORD PTR [%ebp-8], 1
    mov %eax, 0
    add %esp, 16
    pop %ecx
    pop %ebp
    lea %esp, [%ecx-4]
    ret
    .size   main, .-main
    .ident  "GCC: (GNU) 4.2.1 20070719  [FreeBSD]"

所以,我在问 x++ 和 ++x 是否有不同的含义,为什么 GCC 为它们输出一些程序集,不应该有不同的输出?

【问题讨论】:

  • 当你的整个语句只是增量时,它们会做同样的事情 - 它们会增加 s。

标签: c gcc compiler-construction


【解决方案1】:

这是一个写得不好的测试用例的例子。表达式 value++ 或 ++value 的实际结果永远不会被存储,因此编译器可以处理这两种基本类型的等价物。

改用这个例子:

main() { int s = 0, x; x = ++s; return 0; }

main() { int s = 0, x; x = s++; return 0; }

后增量:

(gdb) disas /m main
函数 main() 的汇编代码转储:
1个int main(){
   0x0040138c : 推送 %ebp
   0x0040138d : 移动 %esp,%ebp
   0x0040138f : 和 $0xfffffff0,%esp
   0x00401392 : 低于 $0x10,%esp
   0x00401395 : 调用 0x4018d4

2 个整数 s = 0;
   0x0040139a : 移动 $0x0,0xc(%esp)

3个整数x;
4 x = s++;
   0x004013a2 : 移动 0xc(%esp),%eax
   0x004013a6 : 移动 %eax,0x8(%esp)
   0x004013aa : 包括 0xc(%esp)

5返回0;
   0x004013ae : 移动 $0x0,%eax

6 } 0x004013b3 : 离开
   0x004013b4 : 回复

汇编程序转储结束。
(gdb)

预增量:

(gdb) disas /m main
函数 main() 的汇编代码转储:
1个int main(){
   0x0040138c : 推送 %ebp
   0x0040138d : 移动 %esp,%ebp
   0x0040138f : 和 $0xfffffff0,%esp
   0x00401392 : 低于 $0x10,%esp
   0x00401395 : 调用 0x4018d4

2 个整数 s = 0;
   0x0040139a : 移动 $0x0,0xc(%esp)

3个整数x;
4 x = ++s;
   0x004013a2 : 包括 0xc(%esp)
   0x004013a6 : 移动 0xc(%esp),%eax
   0x004013aa : 移动 %eax,0x8(%esp)

5返回0;
   0x004013ae : 移动 $0x0,%eax

6 } 0x004013b3 : 离开
   0x004013b4 : 回复

汇编程序转储结束。
(gdb)

【讨论】:

  • 我建议return语句是return x。
【解决方案2】:

要查看不同的输出,您需要做的不仅仅是增加。

main() { int s = 0; int a = ++s;返回0; }

++s 和 s++ 如果不使用该值,则执行完全相同的操作。

【讨论】:

    【解决方案3】:

    这叫做“优化”。

    在您的情况下,无论是前置增量还是后置增量(它导致完全相同的逻辑流程),因此汇编输出是相同的。

    这是一个反例:

    /*
     * SAMPLE OUTPUT: i++=1, ++j=3
     */
    #include <stdio.h>
    
    int
    main (int argc, char *argv[])
    {
      int i = 1, j= 2;
      printf ("i++=%d, ++j=%d\n", i++, ++j);
      return 0;
    }
    

    这是 Visual C++ 汇编器的输出。您会注意到 this 示例增加了 *“i”和“j”:

    ; File tmp.c
    ; Line 5
        push    ebp
        mov ebp, esp
        sub esp, 12                 ; 0000000cH
    ; Line 6
        mov DWORD PTR _i$[ebp], 1
        mov DWORD PTR _j$[ebp], 2
    ; Line 7
        mov eax, DWORD PTR _j$[ebp]
        add eax, 1
        mov DWORD PTR _j$[ebp], eax
        mov ecx, DWORD PTR _j$[ebp]
        push    ecx
        mov edx, DWORD PTR _i$[ebp]
        mov DWORD PTR -12+[ebp], edx
        mov eax, DWORD PTR -12+[ebp]
        push    eax
        push    OFFSET FLAT:$SG342
        mov ecx, DWORD PTR _i$[ebp]
        add ecx, 1
        mov DWORD PTR _i$[ebp], ecx
        call    _printf
        add esp, 12                 ; 0000000cH
    ; Line 8
        xor eax, eax
    ; Line 9
        mov esp, ebp
        pop ebp
        ret 0
    _main   ENDP
    

    这里是 gcc 汇编器,带有 -O3。您会注意到它不会进行 任何 增量:编译器只存储最终值 i=1 和 j=3:

    .LC0:
            .string "i++=%d, ++j=%d\n"
            .text
            .p2align 4,,15
    .globl main
            .type   main, @function
    main:
            leal    4(%esp), %ecx
            andl    $-16, %esp
            pushl   -4(%ecx)
            pushl   %ebp
            movl    %esp, %ebp
            pushl   %ecx
            subl    $20, %esp
            movl    $3, 8(%esp)
            movl    $1, 4(%esp)
            movl    $.LC0, (%esp)
            call    printf
            addl    $20, %esp
            xorl    %eax, %eax
            popl    %ecx
    

    最后,这是优化 Visual C++ 版本(带有“/Ox”)。您会注意到它是这三个示例中最短的一个:

    ; File tmp.c
    ; Line 7
        push    3
        push    1
        push    OFFSET FLAT:$SG399
        call    _printf
        add esp, 12                 ; 0000000cH
    ; Line 8
        xor eax, eax
    ; Line 9
        ret 0
    _main   ENDP
    

    【讨论】:

      猜你喜欢
      • 2019-03-05
      • 2020-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-05
      • 2018-03-22
      • 1970-01-01
      相关资源
      最近更新 更多