C/C++ のセグメンテーション違反
C のセグメンテーション違反 または C++ は、プログラムがアクセス許可を持たないメモリ位置にアクセスしようとしたときに発生するエラーです。通常、このエラーはメモリ アクセスが違反された場合に発生し、一般保護違反の一種です。 セグメンテーション違反 セグメンテーションフォールトの略称です。
の コアダンプ プログラムの状態、つまりメモリとプロセッサ内のリソースの記録を指します。 存在しないメモリや他のプロセスが使用しているメモリにアクセスしようとすると、セグメンテーション違反が発生し、コア ダンプが発生します。
プログラムは、実行中にメモリの特定の領域にアクセスできます。まず、スタックは各関数のローカル変数を保持するために使用されます。さらに、実行時にメモリが割り当てられ、ヒープに保存される場合もあります (C++ の新機能で、 無料ストア )。プログラムがアクセスを許可される唯一のメモリは、プログラム自身のメモリ (前述のメモリ) です。セグメンテーション違反は、その領域外にアクセスすると発生します。
セグメンテーション違反は、メモリへのアクセスによって引き起こされる特定の種類のエラーです。 あなたのものではありません :
- コードの一部がメモリ内の読み取り専用の場所またはメモリの解放されたブロックで読み取りおよび書き込み操作を実行しようとすると、それはセグメンテーション フォールトとして知られます。
- メモリ破損を示すエラーです。
一般的なセグメンテーション障害のシナリオ
セグメンテーション違反では、プログラムはアクセスを許可されていないメモリ、または存在しないメモリにアクセスしようとします。セグメンテーション違反を引き起こす可能性のある一般的なシナリオは次のとおりです。
- 文字列リテラルの変更
- 解放されたアドレスにアクセスする
- 配列外のインデックス境界へのアクセス
- scanf() の不適切な使用
- スタックオーバーフロー
- 初期化されていないポインタを逆参照しています
1. 文字列リテラルの変更
文字列リテラルはメモリの読み取り専用セクションに保存されます。そのため、行 *(str+1) = ‘n’ が読み取り専用メモリに書き込もうとするため、以下のプログラムがクラッシュする (セグメンテーション違反エラーが発生する) 可能性があります。
例:
C
// C program to demonstrate segmentation fault> // by modifying a string literal> #include> int> main()> {> > char> * str;> > // Stored in read only part of data segment //> > str => 'GfG'> ;> > // Problem: trying to modify read only memory //> > *(str + 1) => 'n'> ;> > return> 0;> }> |
C++
// C++ program to demonstrate segmentation fault> // by modifying a string literal> #include> using> namespace> std;> int> main()> {> > char> * str;> > // Stored in read only part of data segment //> > str => 'GfG'> ;> > // Problem: trying to modify read only memory //> > *(str + 1) => 'n'> ;> > return> 0;> }> |
出力
タイムアウト: 監視対象のコマンドがコアをダンプしました
/bin/bash: 行 1: 32 セグメンテーション違反タイムアウト 15 秒 ./83b16132-8565-4cb1-aedb-4eb593442235 <83b16132-8565-4cb1-aedb-4eb593442235.in
詳細については、「C の文字列のストレージ」を参照してください。
2. 解放されたアドレスへのアクセス
以下のコードでは、メモリ ブロックを解放した後にポインタ p が逆参照されていますが、これはコンパイラでは許可されていません。このようなポインターはダングリング ポインターと呼ばれ、実行時にセグメント フォールトやプログラムの異常終了を引き起こします。
例:
C
// C program to demonstrate segmentation fault> // by Accessing an address that is freed> #include> #include> int> main(> void> )> {> > // allocating memory to p> > int> * p = (> int> *)> malloc> (8);> > *p = 100;> > // deallocated the space allocated to p> > free> (p);> > // core dump/segmentation fault> > // as now this statement is illegal> > *p = 110;> > printf> (> '%d'> , *p);> > return> 0;> }> |
C++
// C++ program to demonstrate segmentation fault> // by Accessing an address that is freed> #include> using> namespace> std;> int> main(> void> )> {> > // allocating memory to p> > int> * p = (> int> *)> malloc> (> sizeof> (> int> ));> > *p = 100;> > // deallocated the space allocated to p> > free> (p);> > // segmentation fault> > // as now this statement is illegal> > *p = 110;> > return> 0;> }> |
出力
Segmentation Fault
3. 範囲外の配列インデックスへのアクセス
C および C++ では、範囲外の配列インデックスにアクセスすると、セグメンテーション違反やその他の未定義の動作が発生する可能性があります。 C および C++ には配列の境界チェックがありません。 C++ では、std::vector::at() メソッドや if() ステートメントなどのコンテナーを使用すると、範囲外のエラーを防ぐことができます。
例:
C
// C program to demonstrate segmentation> // fault when array out of bound is accessed.> #include> int> main(> void> )> {> > int> arr[2];> > // Accessing out of bound> > arr[3] = 10;> > return> (0);> }> |
C++
// C++ program to demonstrate segmentation> // fault when array out of bound is accessed.> #include> using> namespace> std;> int> main()> {> > int> arr[2];> > // Accessing out of bound> > arr[3] = 10;> > return> 0;> }> |
出力
Segmentation Faults
4. scanf() の不適切な使用
scanf() 関数は、変数のアドレスを入力として期待します。このプログラムでは、n の値は 2 であり、そのアドレスは 1000 であると想定されます。 n を scanf() に渡すと、STDIN からフェッチされた入力は、代わりに 1000 であるべき無効なメモリ 2 に配置されます。これによりメモリ破損が発生し、セグメンテーション違反が発生します。
例:
C
// C program to demonstrate segmentation> // fault when value is passed to scanf> #include> int> main()> {> > int> n = 2;> > scanf> (> '%d'> , n);> > return> 0;> }> |
C++
// C++ program to demonstrate segmentation> // fault when value is passed to scanf> #include> using> namespace> std;> int> main()> {> > int> n = 2;> > cin>> ん;>> > return> 0;> }> |
出力
Segementation Fault
5. スタックオーバーフロー
コードにポインターが 1 つもない場合でも、これはポインター関連の問題ではありません。スタック上のメモリが不足していることが原因です。これは、大きな配列サイズ、多数の再帰呼び出し、多数のローカル変数などが原因で発生する可能性のあるメモリ破損の一種でもあります。
例:
C
// C program to illustrate the> // segmentation fault due to> // stack overflow> #include> int> main()> {> > int> arr[2000000000];> > return> 0;> }> |
C++
// C++ program to illustrate> // the segmentation fault> // due to stack overflow> #include> using> namespace> std;> int> main()> {> > int> array[2000000000];> > return> 0;> }> |
出力
Segmentation Fault
6. バッファオーバーフロー
バッファに格納されているデータがバッファに割り当てられたサイズよりも大きい場合、バッファ オーバーフローが発生し、セグメンテーション フォールトが発生します。 C 言語のメソッドのほとんどは境界チェックを実行しないため、バッファに必要なサイズを割り当て忘れるとバッファ オーバーフローが頻繁に発生します。
例:
C
// C program to illustrate the> // segementation fault due to> // buffer overflow> #include> int> main()> {> > char> ref[20] => 'This is a long string'> ;> > char> buf[10];> > sscanf> (ref,> '%s'> , buf);> > return> 0;> }> |
C++
// C++ program to illustrate the> // segementation fault due to> // buffer overflow> #include> using> namespace> std;> int> main()> {> > char> ref[20] => 'This is a long string'> ;> > char> buf[10];> > sscanf> (ref,> '%s'> , buf);> > return> 0;> }> |
出力
Segmentation Fault
7. 初期化されていないポインタまたは NULL ポインタの逆参照
初期化されていないポインター (ワイルド ポインター) を逆参照するのは一般的なプログラミング エラーであり、未定義の動作が発生する可能性があります。ポインタを有効なポインタとして扱い、その基になる値にアクセスするコンテキストでポインタが使用されると、有効なメモリ位置を指すように初期化されていない場合でも、このエラーが発生します。これにより、データ破損、プログラム エラー、またはセグメンテーション違反が発生する可能性があります。逆参照時の環境や状態に応じて、初期化されていないポインタは異なる結果をもたらす可能性があります。
ご存知のとおり、NULL ポインターはメモリの場所を指していないため、参照を解除するとセグメンテーション違反が発生します。
例:
C
// C program to demonstrate segmentation> // fault when uninitialized pointer> // is accessed> #include> int> main()> {> > int> * ptr;> > int> * nptr = NULL;> > printf> (> '%d %d'> , *ptr, *nptr);> > return> 0;> }> |
C++
// C++ program to demonstrate segmentation> // fault when uninitialized pointer> // is accessed> #include> using> namespace> std;> int> main()> {> > int> * ptr;> > int> * nptr = NULL;> > cout < < *ptr < <> ' '> < < *nptr;> > return> 0;> }> |
出力
Segmentation Fault
セグメンテーション違反を修正するにはどうすればよいですか?
以下の原因に注意することで、セグメンテーション違反を修正できます。
- 文字列リテラルの変更は避けてください。
- ポインターは最も一般的な原因の 1 つであるため、ポインターを使用する場合は注意してください。
- バッファまたはスタックのオーバーフローを避けるために、データを保存する前にバッファとスタックのサイズを考慮してください。
- 配列要素にアクセスする前に境界をチェックします。
- scanf() と printf() は、不正な形式指定子やバッファ オーバーフローを避けるために慎重に使用してください。
全体として、セグメンテーション違反の原因は、その空間内の自分に属さないメモリにアクセスすることです。それを避ける限り、セグメンテーション違反を回避できます。実行してもエラーの原因が見つからない場合は、プログラムのエラー箇所に直接つながるため、デバッガを使用することをお勧めします。