2016-07-01 2 views
2

振る舞う[更新] 2016年7月2日標準機能をオーバーライドすると、予想外に

main.cの

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

size_t strlen(const char *str) { 
    printf("%s\n", str); 
    return 99; 
} 
int main() { 
    const char *str = "AAA"; 
    size_t a = strlen(str); 
    strlen(str); 
    size_t b = strlen("BBB"); 
    return 0; 
} 

は、予想出力は、メインメイン-o GCC -O0でコンパイル

AAA 
AAA 
BBB 

あります。 c:

AAA 

コンパイルウィットH GCC -O3 -o主main.cの

AAA 
AAA 

フラグと対応するASMコード-O0

000000000040057c <main>: 
40057c: 55      push %rbp 
40057d: 48 89 e5    mov %rsp,%rbp 
400580: 48 83 ec 20    sub $0x20,%rsp 
400584: 48 c7 45 e8 34 06 40 movq $0x400634,-0x18(%rbp) 
40058b: 00 
40058c: 48 8b 45 e8    mov -0x18(%rbp),%rax 
400590: 48 89 c7    mov %rax,%rdi 
400593: e8 c5 ff ff ff   callq 40055d <strlen> 
400598: 48 89 45 f0    mov %rax,-0x10(%rbp) 
40059c: 48 c7 45 f8 03 00 00 movq $0x3,-0x8(%rbp) 
4005a3: 00 
4005a4: b8 00 00 00 00   mov $0x0,%eax 
4005a9: c9      leaveq 
4005aa: c3      retq 
4005ab: 0f 1f 44 00 00   nopl 0x0(%rax,%rax,1) 

と-O3有する:フラグ-O0で

0000000000400470 <main>: 
400470: 48 83 ec 08    sub $0x8,%rsp 
400474: bf 24 06 40 00   mov $0x400624,%edi 
400479: e8 c2 ff ff ff   callq 400440 <[email protected]> 
40047e: bf 24 06 40 00   mov $0x400624,%edi 
400483: e8 b8 ff ff ff   callq 400440 <[email protected]> 
400488: 31 c0     xor %eax,%eax 
40048a: 48 83 c4 08    add $0x8,%rsp 
40048e: c3      retq 

、なぜstrlenへの2回目と3回目の呼び出しが、ユーザー定義のstrlenを呼び出さなかったのでしょうか?

そして-O3では、なぜstrlenへの3番目の呼び出しが最適化されていますか?

+1

おそらく、コンパイラが冗長呼び出しを最適化したためですが、それは実装に副作用があると考えると少し奇妙です。 'strlen()'のようなよく知られている関数をどこまで使うのかは不明です。デバッガでコードをシングルステップ実行します。 – unwind

+0

これはgccと仮定し、 '-fno-optimize-strlen'を使ってコンパイルしてみてください – tofro

+1

関数の名前を' my_strlen'に変更して何が起こるかを見てください。そして関数実装で 'extern'キーワードを削除します。 – LPs

答えて

0

おそらく、デフォルトで有効になっている機能でビルドします。あなたは賢明なコールユーザー関数を定義した場合、その後も削除

int strlen(const char*); 

int strlen(const char *s) 

strlen_adder.c でstrlen_adder.h で、strlen function.Soの戻り値の型を変更します#include<string.h>main.cファイル。

1

GCCはstrlen()が内蔵されており、それを組み換えて認識します。したがって、ご使用のバージョンstrlen()は呼び出されません。

​​でコードをコンパイルして、組み込み関数を無効にして、Log in strlenの出力を1回目と3回目のstrlen()呼び出しから2回取得します。おそらく2番目のstrlen()は戻り値が使用されていないので最適化されます。これはおそらくの前に発生します。 GCCはstrlen()の組み込み関数を使用できないことを認識します。それ以外の場合は、という副作用があるので、第2のstrlen()コールを最適化することはできませんメッセージを印刷します。

同様に、店舗のようなものを持つ第二strlen()呼び出しの結果:印刷されたばかりの3倍 "のstrlen()でログインし、"

size_t b = strlen(str); // call 2 

が、私は参照してください。

私は-O3(とや​​なしのいずれか)でコンパイルする場合は前に言ったように、GCC全体 プログラムを離れて最適化し、ので、私はまったくの出力を取得していません。

標準機能の再定義は技術的にはundefined behaviourなので、これはGCCの問題ではないため、GCCは自由にそれを処理します。

+0

私はstrlenを定義したユーザを呼び出すが、strlenへのすべての関数呼び出しが自分の関数を呼び出すわけではないことがわかった。私の質問は、私の目標を達成するために問題を解決するのではなく、それがどのように機能するのかを理解することです。 – mingpepe

+1

標準機能を再定義することは、技術的には未定義の動作です。あなたはこの文書をどのような文書で見ましたか? – mingpepe

+0

'これはGCCが論理仮定のような組み込み音を使用できないことを認識する前に起こります。おかげで、文字列組み込み関数/組み込み関数の最適化を含むさまざまなコンパイラで不具合が見つかったのは、何度も二度目です。 'strcpy'とMSVCに関する他の(無関係な)ものは[ms vC++コンパイラが誤ったコードを最適化する]です(http://stackoverflow.com/questions/38111158/ms-vc-compiler-optimizating-away-erroneous-code) 。 – dxiv

関連する問題