3

ここにCコードがあり、gccでコンパイルしました

char *a="a";
char *d="d";
printf("%d\n", strcmp("a", "d"));
printf("%d\n", strcmp(a, "d"));
printf("%d\n", strcmp(a, d));

-O出力でコンパイルすると

-1
-3
-1

なしでコンパイルすると-O、出力は

-1
-3
-3

出力が異なる理由と のコードはstrcmp何ですか?

4

4 に答える 4

5

出力が異なる理由

重要なのは、戻り値の符号(正、負、またはゼロ) だけだからです。strcmp()+1 または -1 を返す必要はなく、一貫した値を返す必要もありません。strcmp()最初と 3 番目のケースでは、コンパイラが への呼び出しを最適化し、戻り値の場所に -1 を入れていると思われます。2番目のケースでは、関数が実際に呼び出されていると思います。

strcmpのコードは何ですか?

最初の異なる文字の文字コード間の違いを返すように見えるという事実から推測すると、これはglibcのものだと思いますstrcmp():

int
 strcmp (p1, p2)
      const char *p1;
      const char *p2;
 {
   register const unsigned char *s1 = (const unsigned char *) p1;
   register const unsigned char *s2 = (const unsigned char *) p2;
   unsigned char c1, c2;

   do
     {
       c1 = (unsigned char) *s1++;
       c2 = (unsigned char) *s2++;
       if (c1 == '\0')
     return c1 - c2;
     }
   while (c1 == c2);

   return c1 - c2;
 }

編集: @AndreyT は私を信じていないので、GCC 4.2 が私のために生成したアセンブリを次に示します (OS X 10.7.5 64 ビット Intel、デフォルトの最適化レベル - フラグなし):

    .section    __TEXT,__text,regular,pure_instructions
    .globl  _main
    .align  4, 0x90
_main:
Leh_func_begin1:
    pushq   %rbp
Ltmp0:
    movq    %rsp, %rbp
Ltmp1:
    subq    $32, %rsp
Ltmp2:
    leaq    L_.str(%rip), %rax
    movq    %rax, -16(%rbp)
    leaq    L_.str1(%rip), %rax
    movq    %rax, -24(%rbp)
    movl    $-1, %ecx             ; <- THIS!
    xorb    %dl, %dl
    leaq    L_.str2(%rip), %rsi
    movq    %rsi, %rdi
    movl    %ecx, %esi
    movq    %rax, -32(%rbp)
    movb    %dl, %al
    callq   _printf               ; <- no call to `strcmp()` so far!
    movq    -16(%rbp), %rax
    movq    %rax, %rdi
    movq    -32(%rbp), %rsi
    callq   _strcmp               ; <- strcmp()
    movl    %eax, %ecx
    xorb    %dl, %dl
    leaq    L_.str2(%rip), %rdi
    movl    %ecx, %esi
    movb    %dl, %al
    callq   _printf               ; <- printf()
    movq    -16(%rbp), %rax
    movq    -24(%rbp), %rcx
    movq    %rax, %rdi
    movq    %rcx, %rsi
    callq   _strcmp               ; <- strcmp()
    movl    %eax, %ecx
    xorb    %dl, %dl
    leaq    L_.str2(%rip), %rdi
    movl    %ecx, %esi
    movb    %dl, %al
    callq   _printf               ; <- printf()
    movl    $0, -8(%rbp)
    movl    -8(%rbp), %eax
    movl    %eax, -4(%rbp)
    movl    -4(%rbp), %eax
    addq    $32, %rsp
    popq    %rbp
    ret
Leh_func_end1:

    .section    __TEXT,__cstring,cstring_literals
L_.str:
    .asciz   "a"

L_.str1:
    .asciz   "d"

L_.str2:
    .asciz   "%d\n"

    .section    __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support
EH_frame0:
Lsection_eh_frame:
Leh_frame_common:
Lset0 = Leh_frame_common_end-Leh_frame_common_begin
    .long   Lset0
Leh_frame_common_begin:
    .long   0
    .byte   1
    .asciz   "zR"
    .byte   1
    .byte   120
    .byte   16
    .byte   1
    .byte   16
    .byte   12
    .byte   7
    .byte   8
    .byte   144
    .byte   1
    .align  3
Leh_frame_common_end:
    .globl  _main.eh
_main.eh:
Lset1 = Leh_frame_end1-Leh_frame_begin1
    .long   Lset1
Leh_frame_begin1:
Lset2 = Leh_frame_begin1-Leh_frame_common
    .long   Lset2
Ltmp3:
    .quad   Leh_func_begin1-Ltmp3
Lset3 = Leh_func_end1-Leh_func_begin1
    .quad   Lset3
    .byte   0
    .byte   4
Lset4 = Ltmp0-Leh_func_begin1
    .long   Lset4
    .byte   14
    .byte   16
    .byte   134
    .byte   2
    .byte   4
Lset5 = Ltmp1-Ltmp0
    .long   Lset5
    .byte   13
    .byte   6
    .align  3
Leh_frame_end1:


.subsections_via_symbols

そして元のソースコード:

#include <stdio.h>
#include <string.h>

int main()
{
    const char *a = "a";
    const char *d = "d";
    printf("%d\n", strcmp("a", "d"));
    printf("%d\n", strcmp(a, "d"));
    printf("%d\n", strcmp(a, d));

    return 0;
}

そして、それが生成した出力 (より良い証明のためのスクリーンショット):

ここに画像の説明を入力

于 2013-05-09T04:09:13.740 に答える
4

C 標準では、実装で負の値を返すことができます。また、結果が標準に従う限り、実装がライブラリ関数呼び出しの最適化を行うこともできます...したがって、実装はstrcmp、関数を呼び出す代わりにインラインマシン命令を生成するなどして関数を最適化できます。引数が定数の場合、追加の最適化が可能です。したがって、結果が異なる理由は、オプティマイザーがたまたまいくつかのケースで異なるコードを生成するためです。準拠するプログラムは、どの負の値が返されるかを気にすることはできません。

編集:

現時点での私のシステムでは、出力は

-1
-3
-3

これらの結果を生成するためにコンパイラが生成したコードを次に示します (gcc -S で取得)。

    movl    $-1, 4(%esp)
    movl    $LC2, (%esp)
    call    _printf
    movl    $LC1, 4(%esp)
    movl    28(%esp), %eax
    movl    %eax, (%esp)
    call    _strcmp
    movl    %eax, 4(%esp)
    movl    $LC2, (%esp)
    call    _printf
    movl    24(%esp), %eax
    movl    %eax, 4(%esp)
    movl    28(%esp), %eax
    movl    %eax, (%esp)
    call    _strcmp
    movl    %eax, 4(%esp)

ご覧のとおり、strcmp呼び出しは 2 つしかありません。コンパイラは "a" が "d" より小さいことを認識しているため、最初の比較の -1 の結果はコンパイル時に生成されます。-O を使用すると、次のコードが生成されます。

    movl    $-1, 4(%esp)
    movl    $LC0, (%esp)
    call    _printf
    movl    $-1, 4(%esp)
    movl    $LC0, (%esp)
    call    _printf
    movl    $-1, 4(%esp)
    movl    $LC0, (%esp)
    call    _printf
于 2013-05-09T04:16:35.047 に答える
2

私は得ています

 -1
 -3
 -1

-O4Linux 上の GCC 4.1.2 で最適化された ( ) ビルドの出力。コンパイラが生成するコードは次のとおりですmain

main:
.LFB25:
        subq    $8, %rsp
.LCFI0:
        movl    $-1, %esi
        xorl    %eax, %eax
        movl    $.LC0, %edi
        call    printf
        movzbl  .LC1(%rip), %edx
        movzbl  .LC2(%rip), %eax
        movl    %edx, %esi
        subl    %eax, %esi
        jne     .L2
        movzbl  .LC1+1(%rip), %esi
        movzbl  .LC2+1(%rip), %eax
        subl    %eax, %esi
.L2:
        movl    $.LC0, %edi
        xorl    %eax, %eax
        call    printf
        movl    $-1, %esi
        movl    $.LC0, %edi
        xorl    %eax, %eax
        call    printf
        xorl    %eax, %eax
        addq    $8, %rsp
        ret

これは、最初と最後の比較が実際には最適化されていないことを意味しますが、中間の比較は実際には減算によって本質的に実装されていました (これが生成された理由です-3)。この選択的な動作には何のロジックも見当たらないので、おそらくオプティマイザーの癖に過ぎません。

ところで、最適化なしでは、同じ GCC 4.1.2 が生成します

 -1
 -1
 -1

を呼び出すので出力しますstrcmpstrcmpこの標準ライブラリでは、次のように実装されています

<strcmp>           mov    (%rdi),%al
<strcmp+2>         cmp    (%rsi),%al
<strcmp+4>         jne    <strcmp+19>
<strcmp+6>         inc    %rdi
<strcmp+9>         inc    %rsi
<strcmp+12>        test   %al,%al
<strcmp+14>        jne    <strcmp>
<strcmp+16>        xor    %eax,%eax
<strcmp+18>        retq
<strcmp+19>        mov    $0x1,%eax
<strcmp+24>        mov    $0xffffffff,%ecx
<strcmp+29>        cmovb  %ecx,%eax
<strcmp+32>        retq

つまり、次善と見なされる場合でも、意図的に-10またはを返すように実装されています。+1

于 2013-05-09T05:35:32.590 に答える
1

strcmp< 0文字列が等しくない場合に返します。
これは、文字列内で一致しない最初の文字の値が 2 番目の文字列の方が高いことを示しています。正確な正確な値はUnspecifiedです。
定義されている唯一のことは、出力が次のとおりであるかどうかです。

  • ゼロまたは
  • 正または
  • ネガティブ
于 2013-05-09T04:05:43.360 に答える