24

私はたくさんのcharsを取る C でこのコードを持っています

#include<stdio.h> 
# define NEWLINE '\n'
int main()
{

char c;
char str[6];
int i = 0;
while( ((c = getchar()) != NEWLINE))
{
        str[i] = c;
        ++i;
        printf("%d\n", i);
}

return 0;
}

入力は: testtesttest

出力: 1 2 3 4 5 6 7 8 117 118 119 120

私の質問は次のとおりです。

  1. 明らかに配列の容量を超えているのに、範囲外 (セグメンテーション違反) 例外が発生しないのはなぜですか?

  2. 出力の数値が突然非常に大きな数値にジャンプするのはなぜですか?

これを C++ で試したところ、同じ動作が得られました。この理由を誰か説明してもらえませんか?

4

7 に答える 7

32
  1. C は配列の境界をチェックしません。プログラムがアクセス権を持っていないメモリへのポインタを逆参照しようとした場合にのみ、セグメンテーション違反が発生します。単純に配列の末尾を超えると、その動作が発生する可能性は低くなります。未定義の動作はまさにそれです-未定義です。問題なく動作しているように見えるかもしれませんが、その安全性に頼るべきではありません。
  2. 配列の末尾を超えてメモリにアクセスすると、プログラムが未定義の動作を引き起こします。str[i] = cこの場合、書き込みの 1 つが の値を上書きしているように見えますi
  3. この場合、C++ には C と同じ規則があります。
于 2012-02-04T00:11:48.483 に答える
6

配列インデックスにアクセスする場合、C および C++ は境界チェックを行いません。セグメンテーション違反は、割り当てられていないページを読み書きしようとした場合 (または許可されていないページで何かをしようとした場合、たとえば読み取り専用ページに書き込もうとした場合) にのみ発生しますが、ページは通常かなり大きい (数キロバイトの倍数、Mac OS では 4 KB の倍数) ため、多くの場合、オーバーフローする余地がたくさんあります。

配列が (あなたのもののように) スタック上にある場合、スタックは通常かなり大きい (数メガバイトまで) ため、さらに悪化する可能性があります。これは、セキュリティ上の問題の原因でもあります。スタック上の配列の境界を超えて書き込むと、関数の戻りアドレスが上書きされ、任意のコードが実行される可能性があります (有名な「バッファ オーバーフロー」セキュリティ違反)。

読んだときに得られる値は、この特定の場所にたまたま存在するものです。それらは完全に未定義です。

C++ を使用している場合 (幸運にも C++11 を使用できる場合)、標準ではstd::array<T, N>型が定義されています。これは、境界を認識している配列です。at最後を超えて読み込もうとすると、メソッドはスローします。

于 2012-02-04T00:11:14.040 に答える
3

C は配列の境界をチェックしません。

実際、セグメンテーション違反は、具体的には配列の境界を超えることによって生成される実行時エラーではありません。むしろ、オペレーティング システムによって提供されるメモリ保護の結果です。プロセスが属していないメモリにアクセスしようとした場合、または存在しないメモリ アドレスにアクセスしようとした場合に発生します。

于 2012-02-04T00:11:38.440 に答える
1

次のようにコンパイルする必要があります。

gcc -fsanitize=address -ggdb -o test test.c

詳細については、こちらをご覧ください。

于 2015-09-05T19:08:54.920 に答える
1

メモリの割り当ては、見た目よりも複雑です。この場合、変数 "str" は他の変数の隣のスタック上にあるため、未割り当てのメモリは続きません。メモリも通常、ワード境界で整列されます (1 つの「ワード」は 4 ~ 8 バイトです)。おそらく、別の変数の値をいじったり、何らかの「パディング」(ワードの整列を維持するために追加された空のスペース)、または他の何かを完全にいじったりしていた可能性があります。 .

R ..が言ったように、それは未定義の動作です。範囲外の条件により、セグメンテーション違反が発生する可能性があります... または、サイレントメモリ破損が発生する可能性があります。すでに割り当てられているメモリを変更している場合、これはオペレーティング システムによってキャッチされません。これが、C で範囲外エラーが非常に潜行性である理由です。

于 2012-02-04T00:17:45.783 に答える
1

配列の境界外に書き込むと (実際には、結果を使用して何かを読み書きしなくても、ポインター演算/配列添字を実行するだけでも)未定義の動作が発生します。未定義の動作は報告または報告可能なエラーではありません。それはあなたのプログラムが何でもできることを意味します。それは非常に危険であり、あなたはそれを避ける責任があります。C は Java/Python/etc ではありません。

于 2012-02-04T00:12:45.570 に答える
1

C/C++ は境界をチェックしないためです。

配列は、内部的にメモリ内の場所へのポインタです。呼び出すと、次arr[index]のようになります。

type value = *(arr + index);

結果はガベージ値であるため、大きな数値になります (必ずしもそうとは限りません)。初期化されていない変数と同じです。

于 2012-02-04T00:09:09.740 に答える