1

x++と++xを検索したところ、ここですばらしい答えが見つかりました。そこで、gccのアセンブリ出力を見て、x++と++xがどのように実装されているかを確認することにしました。

main(){int s = 0; ++ s; 0を返します。}

例のコンパイル:

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

++の出力:

    .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がそれらのためにいくつかのアセンブリを出力するのか、異なる出力を持つべきではないのかと尋ねています。

4

3 に答える 3

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
関数main()のアセンブラコードのダンプ:
1 int main(){
   0x0040138c:プッシュ%ebp
   0x0040138d:mov%esp、%ebp
   0x0040138f:および$ 0xfffffff0、%esp
   0x00401392:サブ$ 0x10、%esp
   0x00401395: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:0xc(%esp)を含む

5は0を返します。
   0x004013ae:mov $ 0x0、%eax

6} 0x004013b3:残す
   0x004013b4:ret

アセンブラダンプの終了。
(gdb)

プレインクリメント:

(gdb)disas / m main
関数main()のアセンブラコードのダンプ:
1 int main(){
   0x0040138c:プッシュ%ebp
   0x0040138d:mov%esp、%ebp
   0x0040138f:および$ 0xfffffff0、%esp
   0x00401392:サブ$ 0x10、%esp
   0x00401395:0x4018d4に電話

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

3 int x;
4 x = ++ s;
   0x004013a2:0xc(%esp)を含む
   0x004013a6:mov 0xc(%esp)、%eax
   0x004013aa:mov%eax、0x8(%esp)

5は0を返します。
   0x004013ae:mov $ 0x0、%eax

6} 0x004013b3:残す
   0x004013b4:ret

アセンブラダンプの終了。
(gdb)
于 2012-10-06T16:11:23.533 に答える
2

異なる出力を表示するには、単にインクリメントする以上のことを行う必要があります。

main(){int s = 0; int a = ++ s; 0を返します。}

値が使用されていない場合、++sとs++はまったく同じことを行います。

于 2012-10-06T16:10:56.473 に答える
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;
}

これがVisualC++アセンブラの出力です。この例では、*「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

これが-O3のgccアセンブラです。インクリメントは行われないことに気付くでしょう。コンパイラは最終値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」付き)を示します。これらの3つの例の中で最も短いことに気付くでしょう。

; 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
于 2012-10-06T16:11:38.887 に答える