web-dev-qa-db-ja.com

C ++でセグメンテーションエラーが発生する原因は何ですか?

C++のセグメンテーションエラーの一般的な原因のリストに疑問がないことに気づいたので、追加したいと思いました。

当然one正解がないため、コミュニティWikiです。

これは、C++を学習している新しいプログラマーに役立つかもしれません。同意しない場合は、気軽に閉じてください。

41
fluffels

OSにMMU( Memory Management Unit )がある場合のみ、セグメンテーション違反はメモリへの不正アクセスによって引き起こされます。動作。

仮想メモリ(アクセス可能なメモリ全体= 2^(sizeof(pointer_type)*8)(つまり:2^num_bits_in_pointer_type))は、ページまたはセグメントという名前の単位で物理メモリにマップされます(ページングは​​、セグメンテーションを置き換えますが、それらは引き続き使用されます)。

各ページにはいくつかの保護権限があります。読み取りアクセス権のないページから読み取ろうとすると、セグメンテーション違反が発生します。読み取り専用の場所に書き込もうとすると、SIGSEGVが返されます。

ユニット化されたポインターを使用している場合は、別の適切な場所を指しているため、セグメンテーション違反が発生しないことがあります。バインド後に小さな配列の読み取りがある場合、ページの境界を超えていない場合、他のメモリ領域が破損する可能性があります。

また、多くのページがあるため、それらのすべてが実際にマップされるわけではありません。マップされていないページをタッチすると、セグメンテーション違反が発生します。実際、マップされていないページへのアクセスでは、書き込み時のコピー、スワップ時のページ、遅延読み込み、メモリマップされたファイルなどを考慮する必要があります。 ページフォールト処理に関するこの記事 、特にここにある2番目の図も参照してください(ただし、詳細については記事を参照してください)

page fault handling
(ソース: vistech.netでのチャンピオン

主に、ユーザー空間で何が起こるか、SIGSEGVに至るすべてのパスに関心があります。しかし、カーネルスペースも興味深いものです。

38
Mihai Maruseac

NULLポインターの逆参照。

#include <cstddef> //For NULL.
int* p1 = NULL; //p1 points to no memory address
*p1 = 3; //Segfault.
5
fluffels

範囲外の配列へのアクセス(可能):

int ia[10];
ia[10] = 4; // Someone forgot that arrays are 0-indexed! Possible Segfault.
4
Seb Holzapfel

C++を「セグメンテーションフォールト」する方法の多くは、必ずしも保証であるとは限りません。実際、ここに掲載されているほとんどの例はそうです。セグメンテーション違反が発生することなくこれらの操作を実行できる場合、それは単に幸運です(または、見方によっては不運です!)。

これは、実際には、C++を他の言語から分離するものの1つです。未定義の動作。一方、JavaまたはC#では、これらの操作が実行されたときに発生することが保証される「InvalidOperationException」などが発生する可能性があります。C++では、基本的に「undefined behaviour」引き分けの運、そしてあなたはそれが起こることを決して望んでいない。

4
Seb Holzapfel

私のお気に入り:

_#include <iostream>
struct A {
    virtual void f() {
        std::cout << "A::f();\n";
    }
    int i;
};

struct B : A {
    virtual void f() {
        std::cout << "B::f();\n";
    }
    int j;
};

void seti(A* arr, size_t size) {
    for (size_t i = 0; i < size; ++i)
        arr[i].i = 0;
}

int main() {
    B b[10];
    seti(b, 10);
    b[3].f();
}
_

セグメンテーション違反を引き起こす可能性があるほとんどのものと同様に、これも失敗に失敗する可能性があります。たとえば、ideoneではb[3].f()は失敗しますが、b[2].f()は機能します。

3
Anton Golov

明らかな答えは「未定義の動作」ですが、これは経験のないプログラマーに疑問を投げかけます。また、一部のタイプの未定義の動作は、セグメンテーションフォールト(または別のタイプのクラッシュ)を引き起こす可能性が非常に低くなります。セグメンテーション違反の最も頻繁な原因は、一般にポインター関連です。初期化されていないポインター、nullポインター、または以前に解放されたポインターを逆参照します。オブジェクト(配列またはその他)の最後を超えて(または先頭の前ですが、それほど頻繁ではない)アクセスします。不正なポインターキャストの結果を使用する(static_cast派生型へ、オブジェクトが実際にその型を持たない場合、またはほとんどのreinterpret_cast);等.

ただし、ここで留意すべき最も重要な点は、一般に、これらがセグメンテーションフォールトを引き起こすことは保証されておらず、多くの場合、それらが引き起こすセグメンテーションフォールトは完全に無関係な操作でしか発生しないことです。したがって、ローカル配列の末尾を超えて書き込むと、通常は「動作」しますが、スタック上の配列に続くものはすべて変更されます。他のローカル変数(スタック上のオブジェクトのvptrを変更すると、オブジェクトの仮想関数を呼び出そうとしたときのセグメンテーションフォールト、呼び出し関数のフレームポインター(返された後、おそらくその関数でセグメンテーションフォールトを引き起こす)、またはリターンアドレス(あらゆる種類の奇妙な動作を引き起こします。セグメンテーションフォールトまたは違法な命令トラップが発生する可能性が最も高いでしょう。解放されたメモリの終わりを超えて、または既に解放されたポインタを介して書き込むと、空き領域の領域が破損し、後の割り当て(またはかなり)でのセグメンテーション違反が発生する可能性があります。また、まったく関係のない他のオブジェクトを変更して、そのvptrまたはオブジェクト内の他のポインター、またはランダムデータを破損する可能性があります。ここでも、セグメンテーションフォールトがおそらく最良の結果です。破損したデータ)。

1
James Kanze

ポインターの初期化を忘れて、ランダムなメモリーアドレスを残します。注:これはalways segfaultではないかもしれませんが、可能です。

int* p1; //No initialization.
*p1 = 3; //Possible segfault.
0
fluffels

解放されたメモリを逆参照すると、セグメンテーション違反が発生する可能性があります。

SomeClass* someObject = new SomeClass();
delete someObject;
someObject->someMethod();  //Could cause a segfault.
0
fluffels

文字列リテラルを変更しようとしています:

char* mystr = "test";
mystr[2] = 'w';

この[〜#〜] can [〜#〜]はセグメンテーション違反を引き起こします。

0
Aamir