1

I searched for x++ vs ++x and I found a great answer here, So I decide to see the assembly output of gcc to see how x++ and ++x implemented:

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

Compiling the example:

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

Output of ++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]"      

Output for 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]"

So, I am asking if x++ and ++x have different meaning, why GCC outputs the some assembly for them, shouldn't have different output?

Community
  • 1
  • 1
SIFE
  • 5,567
  • 7
  • 32
  • 46
  • 6
    They do the same thing when your whole statement is just the increment - they increment `s`. – chris Oct 06 '12 at 16:09

3 Answers3

6

This is an example of a not-well written test case. The actual result of the expression value++ or ++value is never stored, as such the compiler can handle both equivalent for basic types.

Use this examples instead:

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

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

Post-increment:

(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)

Pre-increment:

(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)
Zeta
  • 103,620
  • 13
  • 194
  • 236
2

To see different output, you need to do more than just increment.

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

++s and s++ do exactly the same thing if the value is not used.

Luna
  • 1,447
  • 1
  • 18
  • 32
2

It's called "optimization".

In your case, it doesn't matter if pre-increment or post-increment (it results in exactly the same logic flow), so the assembly output is identical.

Here's a counter-example:

/*
 * 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;
}

Here's the Visual C++ assembler output. You'll notice that this example increments *both "i" and "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

Here is gcc assembler, with -O3. You'll notice it doesn't do any increments: the compiler just stores the final values i=1 and 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

Finally, here's the optimized Visual C++ version (with "/Ox"). You'll notice it's the shortest of these three examples:

; 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
paulsm4
  • 114,292
  • 17
  • 138
  • 190