注目すべき標準動作には 3 つのタイプがあります。
1/定義された動作。これは、準拠しているすべての実装で機能します。これはご自由にお使いください。
2/実装定義の動作。述べたように、それは実装に依存しますが、少なくともまだ定義されています。実装は、これらの場合に何を行うかを文書化する必要があります。移植性を気にしない場合は、これを使用してください。
3/未定義の動作。何でも起れる。そして、あなたのコンピューター全体が裸の特異点に崩壊し、それ自体、あなたとあなたの同僚の大部分を飲み込むことを含む、あらゆることを意味します. これは絶対に使用しないでください。これまで!真剣に!私をあそこに来させないでください。
4 文字を超える文字と 0 バイトを a にコピーすると、char[5]
未定義の動作になります。
真剣に、なぜあなたのプログラムが 14 文字ではなく 13 文字でクラッシュするのかは問題ではありません。ほぼ確実に、スタック上のクラッシュしない情報を上書きしているので、いずれにせよ、プログラムは間違った結果を生成する可能性が高いでしょう。実際、少なくともクラッシュの可能性がある悪影響に頼る必要がなくなるため、クラッシュの方が優れています。
配列のサイズをより適切なもの (char[14]
この場合は利用可能な情報) に増やすか、対応できる他のデータ構造を使用してください。
アップデート:
余分な 7 文字では問題が発生しないのに 8 文字では問題が発生する理由を知りたがっているように見えるので、 を入力したときに考えられるスタック レイアウトを想像してみましょうmain()
。実際のレイアウトはコンパイラが使用する呼び出し規約に依存するため、「可能」と言います。C スタートアップ コードは と を使用して呼び出しmain()
てargc
いるargv
ため、 の開始時のスタックは、main()
にスペースを割り当てた後、次のchar[5]
ようになります。
+------------------------------------+
| C start-up code return address (4) |
| argc (4) |
| argv (4) |
| x = char[5] (5) |
+------------------------------------+
次のようにバイトHello1234567\0
を書き込む場合:
strcpy (x, "Hello1234567");
に、 and をx
上書きしますが、 から戻ると、それで問題ありません。具体的には、移入、移入、および移入します。実際に使用しようとしない限り、および/またはその後は問題ありません。argc
argv
main()
Hello
x
1234
argv
567\0
argc
argc
argv
+------------------------------------+ Overwrites with:
| C start-up code return address (4) |
| argc (4) | '567<NUL>'
| argv (4) | '1234'
| x = char[5] (5) | 'Hello'
+------------------------------------+
ただし、 に書き込むとHello12345678\0
(余分な "8" に注意してください) 、とおよび戻りアドレスの 1 バイトがx
上書きされるため、C スタートアップ コードに戻ろうとすると、代わりに妖精の国に移動します。argc
argv
main()
+------------------------------------+ Overwrites with:
| C start-up code return address (4) | '<NUL>'
| argc (4) | '5678'
| argv (4) | '1234'
| x = char[5] (5) | 'Hello'
+------------------------------------+
繰り返しますが、これはコンパイラの呼び出し規約に完全に依存します。別のコンパイラが常に配列を 4 バイトの倍数にパディングし、別の 3 文字を書き込むまでコードが失敗しない可能性があります。同じコンパイラでも、アライメントが確実に満たされるように、スタック フレームに異なる方法で変数を割り当てる場合があります。
それが未定義の意味です。何が起こるかわかりません。