[解決済み] スタンフォード大学のチュートリアルとGCCとの衝突
質問
によると
この
の動画(38分あたり)によると、同じローカルバーを持つ2つの関数がある場合、それらは同じスペースを使用するそうです。ということは、次のようなプログラムは
5
. でコンパイルすると
gcc
結果
-1218960859
.なぜ?
プログラムです。
#include <stdio.h>
void A()
{
int a;
printf("%i",a);
}
void B()
{
int a;
a = 5;
}
int main()
{
B();
A();
return 0;
}
リクエスト通り、ディスアセンブラからの出力は以下のとおりです。
0804840c <A>:
804840c: 55 push ebp
804840d: 89 e5 mov ebp,esp
804840f: 83 ec 28 sub esp,0x28
8048412: 8b 45 f4 mov eax,DWORD PTR [ebp-0xc]
8048415: 89 44 24 04 mov DWORD PTR [esp+0x4],eax
8048419: c7 04 24 e8 84 04 08 mov DWORD PTR [esp],0x80484e8
8048420: e8 cb fe ff ff call 80482f0 <printf@plt>
8048425: c9 leave
8048426: c3 ret
08048427 <B>:
8048427: 55 push ebp
8048428: 89 e5 mov ebp,esp
804842a: 83 ec 10 sub esp,0x10
804842d: c7 45 fc 05 00 00 00 mov DWORD PTR [ebp-0x4],0x5
8048434: c9 leave
8048435: c3 ret
08048436 <main>:
8048436: 55 push ebp
8048437: 89 e5 mov ebp,esp
8048439: 83 e4 f0 and esp,0xfffffff0
804843c: e8 e6 ff ff ff call 8048427 <B>
8048441: e8 c6 ff ff ff call 804840c <A>
8048446: b8 00 00 00 00 mov eax,0x0
804844b: c9 leave
804844c: c3 ret
804844d: 66 90 xchg ax,ax
804844f: 90 nop
どのように解決するのですか?
はい、はい、これは 未定義の動作 というのは、初期化されていない変数を使用しているためです。 1 .
しかし、x86 アーキテクチャでは
2
,
この実験はうまくいくはずです
. この値はスタックから "erased" されていませんし、初期化されていないので
B()
で初期化されていないので、スタックフレームが同じであれば、同じ値がまだそこにあるはずです。
私は、あえて推測すると
int a
は
使用される
の内部で
void B()
を使用した場合、コンパイラはそのコードを最適化し、スタックのその場所には5が書き込まれることはありませんでした。 試しに
printf
で
B()
も同様に、うまくいくかもしれません。
また、コンパイラのフラグ、すなわち最適化レベルは、おそらくこの実験に影響を与えるでしょう。最適化を無効にするために
-O0
を gcc に渡して最適化を無効にしてみてください。
編集してください。
あなたのコードをコンパイルしたところ
gcc -O0
(64-bit)でコンパイルしたところ、確かにコールスタックに詳しい人が予想するように、プログラムは5を表示しました。 実際、それは
-O0
. 32 ビットのビルドでは異なる動作になるかもしれません。
免責事項: 絶対にしないでください。 を使用しないでください。 のようなものを実際のコードで使用しないでください。
1 - 議論が行われている 以下 これが正式なUBなのか、それとも単なる予測不可能なものなのかについて。
2 - x64 や、おそらくコールスタックを使用する他のすべてのアーキテクチャ (少なくとも MMU を持つもの) も同様です。
その理由を見てみましょう。
はなかった
が動作した理由を見てみましょう。 これは32ビットで見るのが一番良いので、コンパイル時に
-m32
.
$ gcc --version
gcc (GCC) 4.7.2 20120921 (Red Hat 4.7.2-2)
でコンパイルしました。
$ gcc -m32 -O0 test.c
(最適化無効)でコンパイルしました。これを実行すると、ゴミが表示されます。
を見てみると
$ objdump -Mintel -d ./a.out
:
080483ec <A>:
80483ec: 55 push ebp
80483ed: 89 e5 mov ebp,esp
80483ef: 83 ec 28 sub esp,0x28
80483f2: 8b 45 f4 mov eax,DWORD PTR [ebp-0xc]
80483f5: 89 44 24 04 mov DWORD PTR [esp+0x4],eax
80483f9: c7 04 24 c4 84 04 08 mov DWORD PTR [esp],0x80484c4
8048400: e8 cb fe ff ff call 80482d0 <printf@plt>
8048405: c9 leave
8048406: c3 ret
08048407 <B>:
8048407: 55 push ebp
8048408: 89 e5 mov ebp,esp
804840a: 83 ec 10 sub esp,0x10
804840d: c7 45 fc 05 00 00 00 mov DWORD PTR [ebp-0x4],0x5
8048414: c9 leave
8048415: c3 ret
では
B
で、コンパイラは 0x10 バイトのスタック空間を確保し、私たちの
int a
変数を
[ebp-0x4]
を 5 に変更します。
で
A
であるにもかかわらず、コンパイラは
int a
で
[ebp-0xc]
. つまり、この場合、ローカル変数
は
が同じ場所で終わってしまうのです! そこで
printf()
の呼び出しを
A
を呼び出すと、そのスタックフレームは
A
と
B
を同一とし、プリント
55
.
関連
-
解決済み] g++ コンパイルエラー: ')'トークンの前に一次式があることが予想される
-
エラー: 宣言されていない識別子 'bool' の使用と C コンパイラでの問題点
-
[解決済み] Windows用Cコンパイラ?[クローズド]
-
[解決済み] 1ビットのセット、クリア、トグルはどのように行うのですか?
-
[解決済み] なぜGCCはa*a*a*a*aを(a*a*a)*(a*a*a)に最適化しないのでしょうか?
-
[解決済み] C言語では「?」演算子は何をするのですか?
-
[解決済み] C言語のコードで「:-!」とは何ですか?
-
[解決済み] const int*、const int * const、int const *の違いは何ですか?
-
[解決済み] g++とgccの違いは何ですか?
-
[解決済み] C言語でオブジェクト指向のコードを書くとしたら、どのようにすればよいのでしょうか?[クローズド]
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
構造体の配列--[エラー] '['トークンの前に一次式があることが予想される
-
警告:符号付き整数式と符号なし整数式の比較 [-Wsign-compare]
-
[解決済み] 初期化でポインタ対象の型から修飾語を捨てる
-
[解決済み] ソケットアクセプト - "開かれているファイルが多すぎる"
-
[解決済み] SQLiteのINSERT/per-secondのパフォーマンスを向上させる
-
[解決済み] C言語では「?」演算子は何をするのですか?
-
[解決済み] Cプリプロセッサはなぜ "linux "という単語を定数 "1 "と解釈するのですか?
-
[解決済み] printfにおけるdoubleの正しい書式指定子
-
[解決済み] フリーは、どのように無料化を知っているのですか?
-
[解決済み] なぜalloca()の使用はグッドプラクティスとみなされないのでしょうか?