web-dev-qa-db-ja.com

データが初期化されていないポインターにコピー/スキャン/読み取りされると、クラッシュまたは「セグメンテーションフォールト」

この質問は、自然のすべてのよくある質問の参照として使用することを意図しています。

初期化されていないポインターが指すアドレスにデータをコピー/スキャンすると、なぜ不思議なクラッシュまたは「セグメンテーションフォールト」が発生するのですか?

例えば:

char* ptr;
strcpy(ptr, "hello world"); // crash here!

または

char* ptr;
scanf("%s", ptr); // crash here!
42
Lundin

ポインターは特別なタイプの変数であり、別の変数のアドレスのみを含めることができます。データを含めることはできません。 「データをポインタにコピー/保存」することはできません-それは意味をなしません。ポインタは、他の場所に割り当てられたデータを指すようにのみ設定できます。

これは、ポインターを意味のあるものにするために、常に有効なメモリー位置を指している必要があることを意味します。たとえば、スタックに割り当てられたメモリを指すことができます。

{
  int data = 0;
  int* ptr = &data;
  ...
}

または、ヒープに動的に割り当てられたメモリ:

int* ptr = malloc(sizeof(int));

初期化される前にポインタを使用することは常にバグです。有効なメモリをまだポイントしていません。

これらの例はすべて、プログラムのクラッシュ、または「セグメンテーションフォールト」などの他の種類の予期しない動作を引き起こす可能性があります。

/*** examples of incorrect use of pointers ***/

// 1.
int* bad;
*bad = 42;

// 2.
char* bad;
strcpy(bad, "hello");

代わりに、ポインターが(十分な)割り当てられたメモリを指すようにする必要があります。

/*** examples of correct use of pointers ***/

// 1.
int var;
int* good = &var;
*good = 42;

// 2.
char* good = malloc(5 + 1); // allocates memory for 5 characters *and*  the null terminator
strcpy(good, "hello");

NULLを指すようにすることで、明確に定義された "nowhere"を指すようにポインターを設定することもできます。これにより、これはnullポインターになります。これは、有効なメモリーを指さないことが保証されているポインターです。これは、ポインターを完全に初期化しないままにすることとは異なります。

int* p1 = NULL; // pointer to nowhere
int* p2;        // uninitialized pointer, pointer to "anywhere", cannot be used yet

ただし、nullポインターが指すメモリにアクセスしようとすると、初期化されていないポインターを使用する場合と同様の問題、つまりクラッシュまたはセグメンテーションエラーが発生する可能性があります。最良の場合、システムは、アドレスnullにアクセスしようとしていることに気付き、「nullポインター例外」をスローします。

NULLポインター例外バグの解決策は同じです。使用する前に有効なメモリーを指すようにポインターを設定する必要があります。


さらに読む:

無効なデータを指すポインター
ポインターを使用して異なる関数からローカル変数にアクセスする方法?
スコープ外でローカル変数のメモリにアクセスできますか?

セグメンテーション障害と原因
セグメンテーション違反とは
「char * s」で初期化され、「char s []」ではなく初期化された文字列に書き込むときにセグメンテーションフォールトが発生するのはなぜですか?
char s []とchar * sの違いは何ですか?
セグメンテーション障害の一般的な理由の決定的なリスト
バスエラーとは

32
Lundin
  1. ポインタはメモリの場所のみを指します。ポインタを作成しましたが、まだメモリの場所にバインドしていません。 strcpyは、次の署名のような2つの文字配列を指す2つのポインター(最初のものは定数であってはなりません)を渡すことを望んでいます。

    _char * strcpy ( char * destination, const char * source );
    _

    サンプル使用法:

    _char* ptr = malloc(32);  
    strcpy(ptr, "hello world");
    _
    _char str[32];  
    strcpy(str, "hello world");
    _
  2. 次のコードスニペットを試して、改行文字に達するまで文字列を読み取ることができます(* _"%[^\t\n]s"_(tab、newline)または_"%[^ \t\n]s"_(- スペース、タブ、改行))。

    _char *ptr = malloc(32);
    scanf("%31[^\n]", ptr);
    _

    (実際には、scanf()!からの戻り値を確認することを忘れないでください。)

3
lonesomecodeboy

これは、pointerchar* ptrの-​​未割り当てメモリがあるために発生します。この場合、動的に割り当てるポインターのメモリが必要です。

dynamic memory allocationには、2つの関数malloc()およびcalloc()を使用できます。

このコードを試してください:-

char* ptr;
ptr = malloc(50); // allocate space for 50 characters.
strcpy(ptr, "hello world");

*ptrを使用する場合は、忘れずにメモリの割り当て解除*ptrに割り当ててください。これはfree()関数を使用して実行できます。

free(ptr);  // deallocating memory.

動的に割り当てられたメモリのサイズは、realloc()を使用して変更できます。

char *tmp = realloc(ptr, 100); // allocate space for 100 characters.
if (! tmp) {
    // reallocation failed, ptr not freed
    perror("Resize failed");
    exit(1);       
}
else {
    // reallocation succeeded, old ptr freed
    ptr = tmp;
}

ほとんどの場合、"セグメンテーションフォールト"は、メモリ割り当てまたは配列範囲外のケースのエラーが原因で発生します。

0
anoopknr

mallocstrlen、およびstrcpyを使用する代わりに、文字列の変更可能なコピーを作成するために、POSIX Cライブラリには<string.h>strdupという便利な関数があります。割り当てられたストレージ期間とともに、渡されたヌル終了文字列のコピーを返します。使用後、ポインタはfreeで解放されます:

char* ptr;
ptr = strdup("hello world");
ptr[0] = 'H';
puts(ptr);
free(ptr);
0
Antti Haapala

この場合ではありませんが、最初に頻繁に発生する状況の1つは、単一引用符を使用して、次のような文字列リテラルを定義しようとした場合です。

char ptr[5];
strcpy(ptr, 'hello'); // crash here!
            ^     ^   // because of ' instead "

Cでは、「h」は1文字のリテラルで、「h」は「h」とヌルターミネータ/ 0(2文字の配列)を含む文字列リテラルです。また、Cでは、文字リテラルのタイプはです。つまり、sizeof 'h'は4(32ビット)で、sizeof(char)は1です。

char h = 'h';
printf("Size: %d\n",sizeof(h));     //Size: 1
printf("Size: %d\n",sizeof('h'));   //Size: 4
0