编译器实现中x ++和++ x之间的区别

时间:2012-10-06 16:08:00

标签: c gcc compiler-construction

我搜索了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为它们输出一些汇编,不应该有不同的输出?

3 个答案:

答案 0 :(得分:6)

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

请改用此示例:

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

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

后递增:

(gdb) disas /m main
Dump of assembler code for function main():
1       int main(){
   0x0040138c :     push   %ebp
   0x0040138d :     mov    %esp,%ebp
   0x0040138f :     and    $0xfffffff0,%esp
   0x00401392 :     sub    $0x10,%esp
   0x00401395 :     call   0x4018d4 

2               int s = 0;
   0x0040139a :    movl   $0x0,0xc(%esp)

3               int x;
4               x = s++;
   0x004013a2 :    mov    0xc(%esp),%eax
   0x004013a6 :    mov    %eax,0x8(%esp)
   0x004013aa :    incl   0xc(%esp)

5               return 0;
   0x004013ae :    mov    $0x0,%eax

6       }   0x004013b3 :   leave
   0x004013b4 :    ret

End of assembler dump.
(gdb)

预增量:

(gdb) disas /m main
Dump of assembler code for function main():
1       int main(){
   0x0040138c :     push   %ebp
   0x0040138d :     mov    %esp,%ebp
   0x0040138f :     and    $0xfffffff0,%esp
   0x00401392 :     sub    $0x10,%esp
   0x00401395 :     call   0x4018d4 

2               int s = 0;
   0x0040139a :    movl   $0x0,0xc(%esp)

3               int x;
4               x = ++s;
   0x004013a2 :    incl   0xc(%esp)
   0x004013a6 :    mov    0xc(%esp),%eax
   0x004013aa :    mov    %eax,0x8(%esp)

5               return 0;
   0x004013ae :    mov    $0x0,%eax

6       }   0x004013b3 :   leave
   0x004013b4 :    ret

End of assembler dump.
(gdb)

答案 1 :(得分:2)

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

  

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

如果没有使用该值,那么++ s和s ++会做同样的事情。

答案 2 :(得分:2)

它被称为“优化”。

在您的情况下,如果预增量或后增量(它导致完全相同的逻辑流程)无关紧要,因此汇编输出是相同的。

这是一个反例:

/*
 * 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 ++汇编程序输出。您会注意到示例会增加*“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