15

私はこの問題を非常に大きなアプリケーションで発見し、そこから SSCCE を作成しました。-O2コードに未定義の動作があるか、それとも壊れているかはわかりません。

それをコンパイルすると、 5gcc a.c -o a.exe -O2 -Wall -Wextra -Werrorが出力されます。

ただし、2 つのコメント行のうちの 1 行のコメントを外したり(例: )、コメントを外したり (インライン化を防止)せずにコンパイルすると、 25が出力されます。-O2-O1

#include <stdio.h>
#include <stdlib.h>
// __attribute__((noinline)) 
int f(int* todos, int input) {
    int* cur = todos-1; // fixes the ++ at the beginning of the loop
    int result = input;
    while(1) {
        cur++;
        int ch = *cur;
        // printf("(%i)\n", ch);
        switch(ch) {
            case 0:;
                goto end;
            case 1:;
                result = result*result;
            break;
        }
    }
    end:
    return result;
}
int main() {
    int todos[] = { 1, 0}; // 1:square, 0:end
    int input = 5;
    int result = f(todos, input);
    printf("=%i\n", result);
    printf("end\n");
    return 0;
}

GCC のオプションは-O2この小さなプログラムを壊していますか、それともどこかで未定義の動作をしていますか?

4

2 に答える 2

15
int* cur = todos-1;

未定義の動作を呼び出します。todos - 1無効なポインタ アドレスです。

強調鉱山:

(C99, 6.5.6p8) 「ポインタ オペランドと結果の両方が同じ配列オブジェクトの要素を指している場合、または配列オブジェクトの最後の要素の 1 つ後ろを指している場合、評価はオーバーフローを生成しません。そうでない場合、動作は未定義です。 . "

于 2014-05-15T16:08:10.263 に答える
4

@ouahの回答を補足して、これはコンパイラが何をしているかを説明しています。

参照用に生成されたアセンブラ:

  400450:       48 83 ec 18             sub    $0x18,%rsp
  400454:       be 05 00 00 00          mov    $0x5,%esi
  400459:       48 8d 44 24 fc          lea    -0x4(%rsp),%rax
  40045e:       c7 44 24 04 00 00 00    movl   $0x0,0x4(%rsp)
  400465:       00 
  400466:       48 83 c0 04             add    $0x4,%rax
  40046a:       8b 10                   mov    (%rax),%edx

ただし、 in を追加するprintfmain():

  400450:       48 83 ec 18             sub    $0x18,%rsp
  400454:       bf 84 06 40 00          mov    $0x400684,%edi
  400459:       31 c0                   xor    %eax,%eax
  40045b:       48 89 e6                mov    %rsp,%rsi
  40045e:       c7 04 24 01 00 00 00    movl   $0x1,(%rsp)
  400465:       c7 44 24 04 00 00 00    movl   $0x0,0x4(%rsp)
  40046c:       00 
  40046d:       e8 ae ff ff ff          callq  400420 <printf@plt>
  400472:       48 8d 44 24 fc          lea    -0x4(%rsp),%rax
  400477:       be 05 00 00 00          mov    $0x5,%esi
  40047c:       48 83 c0 04             add    $0x4,%rax
  400480:       8b 10                   mov    (%rax),%edx

具体的には(printfバージョンでは)、これらの2つの命令はtodo配列にデータを入力します

  40045e:       c7 04 24 01 00 00 00    movl   $0x1,(%rsp)
  400465:       c7 44 24 04 00 00 00    movl   $0x0,0x4(%rsp)

printfこれは、何らかの理由で2 番目の要素のみを割り当てる非バージョンには明らかに欠けています。

  40045e:       c7 44 24 04 00 00 00    movl   $0x0,0x4(%rsp)
于 2014-05-15T16:22:33.200 に答える