1

数週間前に C を始めて以来、これら 2 つの並列フレーズを目にしてきました。C コンパイラの知識を持っている人が、どちらがより良いコードにつながるかを教えてくれる必要があります。

バージョン1:

char s[]="aString",*sp=&s,c;
while(c=*sp++){
operation(c);
}

バージョン 2:

char s[]="aString",*sp=&s;
for(;*sp;sp++){
 operation(*sp);
}


バージョン 2 にはいくつかの繰り返しの差分が含まれていることは理解していますが、バージョン 1 は常にバージョン 2 よりも優れているのでしょうか? そうでない場合、いくつかの典型的な例外は何ですか?

4

3 に答える 3

3

合理的な最適化レベルでは、それらはまったく同じになると思います-試しましたか?

編集:

確認したかったのでやってみました。これが私のサンプルプログラムです(ポインターの不一致エラーも修正しました):

void operation(char);

void f1(void)
{
    char s[]="aString",*sp=s,c;
    while(c=*sp++) {
        operation(c);
    }
}

void f2(void)
{
    char s[]="aString",*sp=s;
    for(;*sp;sp++) {
         operation(*sp);
    }
}

Macで clang at でコンパイルし-O3たオブジェクト ファイルは次のとおりです。

example.o:
(__TEXT,__text) section
_f1:
0000000000000000    pushq   %rbp
0000000000000001    movq    %rsp,%rbp
0000000000000004    pushq   %rbx
0000000000000005    pushq   %rax
0000000000000006    movq    $0x00676e6972745361,%rax
0000000000000010    movq    %rax,0xf0(%rbp)
0000000000000014    movb    $0x61,%al
0000000000000016    leaq    0xf1(%rbp),%rbx
000000000000001a    nopw    _f1(%rax,%rax)
0000000000000020    movsbl  %al,%edi
0000000000000023    callq   _operation
0000000000000028    movb    (%rbx),%al
000000000000002a    incq    %rbx
000000000000002d    testb   %al,%al
000000000000002f    jne 0x00000020
0000000000000031    addq    $0x08,%rsp
0000000000000035    popq    %rbx
0000000000000036    popq    %rbp
0000000000000037    ret
0000000000000038    nopl    _f1(%rax,%rax)
_f2:
0000000000000040    pushq   %rbp
0000000000000041    movq    %rsp,%rbp
0000000000000044    pushq   %rbx
0000000000000045    pushq   %rax
0000000000000046    movq    $0x00676e6972745361,%rax
0000000000000050    movq    %rax,0xf0(%rbp)
0000000000000054    movb    $0x61,%al
0000000000000056    leaq    0xf1(%rbp),%rbx
000000000000005a    nopw    _f1(%rax,%rax)
0000000000000060    movsbl  %al,%edi
0000000000000063    callq   _operation
0000000000000068    movb    (%rbx),%al
000000000000006a    incq    %rbx
000000000000006d    testb   %al,%al
000000000000006f    jne 0x00000060
0000000000000071    addq    $0x08,%rsp
0000000000000075    popq    %rbx
0000000000000076    popq    %rbp
0000000000000077    ret

ご覧のとおり、文字通り同一です。

于 2012-08-05T04:44:02.940 に答える
1

最近では、優れたコンパイラは、これらのループをほとんど同じようにコンパイルします。

c入力が短い場合やコードを読みやすくする場合は、一時変数を使用することをお勧めします。ただし、ある種の想像上のパフォーマンス上の利点のために、このようなことをしないでください。

于 2012-08-05T04:44:24.857 に答える
0

そのソルトに値する最適化コンパイラは、それらを同じアセンブリコードに最適化します。プロファイラーでこれを測定し、ボトルネックであると判断しない限り、時期尚早に最適化しようとしないでください。

値が複数回アクセスされた場合、基になる文字列が変更されていないことをコンパイラが確認できない特定の状況があります。その場合、メモリ アクセスを複数回行う必要があります。そのような場合、文字を変数にキャッシュすることには非常にわずかな利点がありますが、それでも利点はごくわずかです。このようなもの:

for( ; *sp; sp++) {
    operation1(*sp);
    some_function(sp);
    operation2(*sp);
}

some_functionこの場合、コンパイラは基になる文字列を変更するかどうかを認識できない可能性があるため*sp、すべての場合で正しい動作を保証するために、メモリから 2 回読み取る必要があります。

于 2012-08-05T04:44:13.883 に答える