次のコードは、array[10] = 22 または array[9999] = 22 でセゴートしますか?
セグメント障害が発生する前にコード全体が実行されるかどうかを把握しようとしています。(C 言語で)。
#include <stdio.h>
int main(){
int array[10];
int i;
for(i=0; i<9999; ++i){
array[i] = 22;
}
return 0;
}
次のコードは、array[10] = 22 または array[9999] = 22 でセゴートしますか?
セグメント障害が発生する前にコード全体が実行されるかどうかを把握しようとしています。(C 言語で)。
#include <stdio.h>
int main(){
int array[10];
int i;
for(i=0; i<9999; ++i){
array[i] = 22;
}
return 0;
}
状況によって異なります...array[9]の後のメモリがクリーンな場合、もちろん1つが占有されているメモリのセグメントに到達するまで、何も起こらない可能性があります。
コードを試して、次を追加します。
printf("%d\n",i);
ループ内で、クラッシュして燃えるタイミングがわかります。596から2380までのさまざまな結果が得られます。
デバッガを使用しますか?
$ gcc -g seg.c -o so_segfault
$ gdb so_segfault
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
(gdb) run
Starting program: /.../so_segfault
Program received signal SIGSEGV, Segmentation fault.
0x080483b1 in main () at seg.c:7
7 array[i] = 22;
(gdb) print i
$1 = 2406
(gdb)
実際、これをもう一度実行すると、同じ値の i に対して常にセグメンテーション違反が発生するとは限らないことがわかります。確かなことは、i>=10 の場合に発生することですが、クラッシュする i の値を決定する方法はありません。これは決定論的ではないためです。メモリの割り当て方法に依存します。array[222] までメモリが空いている場合 (つまり、他のプログラムが使用しない場合)、メモリは i=222 まで続きますが、i>=10 の他の値でもクラッシュする可能性があります。
答えは多分です。C 言語は、この場合に何が起こるべきかについて何も言いません。これは未定義の動作です。コンパイラは、問題を検出したり、問題を処理したり、プログラムを終了したりする必要はありません。そして、それは何もしません。
自分のものではないメモリに書き込むと、実際には次の 3 つのいずれかが発生する可能性があります。
範囲外の書き込み: やらないでください。C 言語は、それが発生したときに何も通知しないため、自分で監視する必要があります。
コードがクラッシュする時期と場合は決定論的ではありません。コードを実行しているプラットフォームによって異なります。
array
はスタック変数であるため、コンパイラは10 * sizeof(int)
スタック上のバイトを予約します。コンパイラが他のローカル変数をどのように配置するか、およびスタックがどのように成長するかに応じて、のi
直後に来る場合がありますarray
。ダニエルの提案に従い、printf
声明を出すと、興味深い効果に気付くかもしれません。私のプラットフォームでi = 10
は、、array[10] = 22
clobbersi
と次の割り当てはになりarray[23]
ます。
セグメンテーション違反は、ユーザーコードがアクセスできないページに触れようとすると発生します。この場合、スタックが十分に小さく、9999回の反復がスタックからなくなると、1つ取得されます。
array
代わりに(を使用して)ヒープに割り当てた場合malloc()
は、ページ境界の終わりを超えたときにSIGSEGVを取得します。10バイトの割り当てでも、ページ全体が返されます。ページサイズはプラットフォームによって異なります。一部のmallocデバッガーは、配列が範囲外の場合にフラグを立てることができますが、ページの最後から実行したときにハードウェアが関与しない限り、SIGSEGVを取得できないことに注意してください。
コードがセグメンテーション違反になる場所は、使用しているコンパイラ、運、および問題のプログラムのその他のリンクの詳細によって異なります。ほとんどの場合、セグメンテーション違反は発生しませんi == 10
。それは配列の外にありますが、ほぼ確実に、その場所でプロセスにメモリが割り当てられます。ただし、配列の境界を超え続けると、最終的にメモリをプロセスに割り当てたままにし、セグメンテーション違反を起こすことになります。
ただし、配列の境界を超えて書き込むと、同じスタック フレーム内の他の自動変数が上書きされる可能性があります。これらの変数のいずれかがポインター (または後で使用される配列インデックス) である場合、これらの破損した値を参照すると、segfault が発生する可能性があります。(破損した正確な値と、プロセスに割り当てられていないメモリを参照するかどうかによって異なります。)
これはそれほど決定論的ではありません。
プロセス専用メモリの外部にアクセスすると、セグメンテーション違反が発生します。これは簡単には予測できません。i == 10の場合、配列の外にありますが、プロセスのメモリ内にある可能性があります。これは、プロセスのメモリがどのように割り当てられたかによって異なります。(通常は)知る方法がありません(OSのメモリマネージャによって異なります)。したがって、セグメンテーション違反はi = 10-9999のいずれかで発生するか、まったく発生しない可能性があります。
より一般的には、デバッガーを使用して、Linuxシステムでセグメンテーション違反が発生する場所を特定できます。たとえば、gdbを使用するには、-gフラグを使用してデバッグシンボルを使用してプログラムをコンパイルします。例:
gcc -g segfault.c -o segfault
次に、プログラムでgdbを呼び出し、-argsフラグを使用して引数を呼び出します。例:
gdb --args ./segault myarg1 myarg2 ...
次に、デバッガーが起動しrun
たら、と入力すると、プログラムはSIGSEGVを受信するまで実行され、そのシグナルを受信したときにソースコードのどこにあったかがわかります。
このような問題を調査するには、GDBを使用することをお勧めします:http ://www.gnu.org/software/gdb/documentation/