14

次のように定義された構造体があるとします

struct my_struct
{
     int num;
};

....

ここにポインタがmy_structあり、インクリメントを行いたいnum

void foo(struct my_struct* my_ptr)
{
     // increment num
     // method #1
     my_ptr->num++;

     // method #2
     ++(my_ptr->num);

     // method #3
     my_ptr->++num;

}

これらの 3 つのインクリメント方法はnum同じことを行いますか? ところで、プレインクリメントはポストインクリメントよりも効率的であるというのは本当ですか?

ありがとう!

4

2 に答える 2

10

最初の 2 つは同じ効果があります (そのような単独の行にある場合) が、3 番目の方法は有効な C コードではありません (++そこに置くことはできません)。

効率に関しては、違いはありません。人々が話しているのを聞いたことがあるかもしれない違いは、C++ では、反復子などの非ポインター データ型をインクリメントする場合です。場合によっては、事前インクリメントの方が高速になることがあります。

生成されたコードは、GCC Explorerを使用して表示できます。

void foo(struct my_struct* my_ptr)
{
    my_ptr->num++;
}

void bar(struct my_struct* my_ptr)
{
    ++(my_ptr->num);
}

出力:

foo(my_struct*):                      # @foo(my_struct*)
    incl    (%rdi)
    ret

bar(my_struct*):                      # @bar(my_struct*)
    incl    (%rdi)
    ret

ご覧のとおり、まったく違いはありません。

最初の 2 つの唯一の違いは、式で使用する場合です。

my_ptr->num = 0;
int x = my_ptr->num++; // x = 0

my_ptr->num = 0;
int y = ++my_ptr->num; // y = 1
于 2012-09-30T17:05:35.673 に答える
3

num の値をインクリメントすることが唯一の目的である場合、1 番目と 2 番目のメソッドは同じ意図した結果を呼び出し先メソッドにもたらします。

ただし、コードを次のように変更すると、gcc によって生成されたコード (アセンブリ レベルのコード) との違いがわかります。

struct my_struct
{
     int num;
};

void foo(struct my_struct* my_ptr)
{
        printf("\nPost Increment: %d", my_ptr->num++);
}

int main()
{
        struct my_struct a;
        a.num = 10;

        foo(&a);
}

次を使用してコンパイルします: gcc -masm=intel -S structTest.c -o structTest.s これは、アセンブリ コードを生成するように gcc に要求します。

テキスト エディターで structTest.s を開きます。

foo:
.LFB0:
         push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        **mov     QWORD PTR [rbp-8], rdi**
        mov     rax, QWORD PTR [rbp-8]
        mov     eax, DWORD PTR [rax]
        mov     edx, eax
        **lea     ecx, [rax+1]**
        mov     rax, QWORD PTR [rbp-8]
        mov     DWORD PTR [rax], ecx
        mov     eax, OFFSET FLAT:.LC0
        mov     esi, edx
        mov     rdi, rax
        mov     eax, 0
        call    printf
        leave
        ret
        .cfi_endproc

main:
.LFB1:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        **mov     DWORD PTR [rbp-16], 10
        lea     rax, [rbp-16]
        mov     rdi, rax
        call    foo**
        leave
        ret
        .cfi_endproc

また、操作をプリインクリメントに変更すると、次のコードが生成されます。

foo:
.LFB0:
        .cfi_startproc
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        **mov     QWORD PTR [rbp-8], rdi**
        mov     rax, QWORD PTR [rbp-8]
        mov     eax, DWORD PTR [rax]
        **lea     edx, [rax+1]**
        mov     rax, QWORD PTR [rbp-8]
        **mov     DWORD PTR [rax], edx**
        mov     rax, QWORD PTR [rbp-8]
        **mov     edx, DWORD PTR [rax]**
        mov     eax, OFFSET FLAT:.LC0
        mov     esi, edx
        mov     rdi, rax
        mov     eax, 0
        call    printf
        leave
        ret
        .cfi_endproc

したがって、2 番目のケースでは、コンパイラが num 値をインクリメントし、この num 値を printf() に渡すことがわかります。

パフォーマンスの面では、ポストインクリメントがより効率的であると期待しています。これは、メモリ位置に触れる回数が少ないためです。

上記のコードでは、重要な行は ** の間にマークされています。

于 2012-09-30T17:52:48.150 に答える