につながる++グラムにおける記号「_end」を使用して、次のC++ソースコードを考えてみましょう。セグメンテーションフォールト
int _end[1050];
int main() {
for (int i = 0; i < 1050; i++)
_end[i] = 0;
return 0;
}
コンパイルライン:g++ main.cpp -o main -O0
このコードを実行するには、GCC-4.8を使用した場合、障害をセグメンテーションにつながります。 4とclang-3.6.0をUbuntu 14.04でリリースしました。奇妙な振る舞いは、シンボル_end
が、静的に割り当てられた配列_end
の終わりを指し、その先頭ではないことを示しています。 _end
をend_
に置き換えると、すべて正常に動作します。
$ g++ main.cpp -o main.s -O0 -S
$ g++ main2.cpp -o main2.s -O0 -S
$ diff main.s main2.s
1,2c1,2
< .file "main.cpp"
< .globl _end
---
> .file "main2.cpp"
> .globl end_
5,7c5,7
< .type _end, @object
< .size _end, 4200
< _end:
---
> .type end_, @object
> .size end_, 4200
> end_:
25c25
< movl $0, _end(,%rax,4)
---
> movl $0, end_(,%rax,4)
:我々は-Sコマンドライン引数を提供することにより、出力にアセンブリコードをgccのを頼む場合
また、「_end」と、他の配列名とバージョンとバージョンの間に有意差は存在しません
しかし、我々は実行可能ファイルをダンプし、それらに対して差分を実行するためにobjdumpの使用している場合、我々は、使用されるアドレスは、さらに必要に応じてより4200 = 4×1050バイトである_end
バージョンでそれを見るだろう。
$ g++ main.cpp -o main -O0
$ g++ main2.cpp -o main2 -O0
$ objdump -d main >main.dump
$ objdump -d main2 > main2.dump
$ diff main.dump main2.dump
2c2
< main: формат файла elf64-x86-64 // "File format" in Russian
---
> main2: формат файла elf64-x86-64
123c123
< 4004ff: c7 04 85 c8 20 60 00 movl $0x0,0x6020c8(,%rax,4)
---
> 4004ff: c7 04 85 60 10 60 00 movl $0x0,0x601060(,%rax,4)
限り私は知っている、gccのコンパイラは、variabそれが望むようにアンダースコアで始まるles、i。 e。これはあなたのコードでそのようなシンボルを使うのは悪い習慣です。しかし、私の質問は、本当にここで何が起こるのですか?なぜ_end
は、割り当てられた配列の最後のアドレスに置き換えられますか? "-S"コマンドライン引数を使用すると違いはありませんが、実際には作成されたバイナリに違いはありますか?この場合、gccとclangは同じように動作するわけではなく、それは私にとっても不思議です。
まさに私が必要なもの、感謝!しかし、なぜこのコードをコンパイルしている間、 "-S"コマンドライン引数は何も疑わしいものではないのですか? –
@ MaximAkhmedovおそらく '_end'は他のポインタと同様のポインタなので、配列に代入するとポインタの算術演算が行われるからです。 – vsoftco