2017-08-10 4 views
0

私はAbort trap 6を知っています。あなたが所有していないメモリの一部に書き込んでいるときに発生しますが、K & R演習中に面白いことが分かりました。C特定の条件の下でのみ `Abort trap:6`

/* strcat: copies t to the end of s */ 
void _strcat(char *s, char *t) { 
    while (*s != '\0') { 
     s++; 
    } 
    while ((*s++ = *t++) != '\0') { } 
} 

#include <stdio.h> 
int main() { 
    char s[7] = "hello, "; 
    char *t = "world"; 

    _strcat(s, t); 
    printf("%s\n", s); 
    return 0; 
} 

char s[7] = "hello, "である。

アレイのサイズが7である場合、"hello, "Abort trap 6の正確な長さは、_strcatは、メモリの初期化部を越えて文字を追加しても、印刷されません。

8〜12の数値はAbort trap 6ですが、13以上の数値は問題ありません。

ので、2つの疑問が生じ:

  1. 7がありながら、なぜchar s[12] = "hello, "大丈夫ではないでしょうか? 13のサイズの配列が末尾に'\0'のために必要ですが、なぜ7は大丈夫でしたか?それは8ではありませんか?
  2. なぜ、7は大丈夫ですか?サイズ8-12には、サイズ7の文字配列と同じように"hello, "が含まれており、元の配列サイズを超えて書き込まれていますが、7はそれ以外の文字ではなくなります。
+0

未定義の動作である必要がないため-string初期化子 Initializer error

それについて不平を言います一貫性のある結果をもたらす。定義されていない動作が呼び出されました。あなたはあなたが得るものを手に入れます。 –

+0

@JonathanLeffler未定義のビヘイビアはどれですか? – BridgeTheGap

+0

サイズが7の配列に5文字を追加すると、これはヌルで終了する文字列ではありません。 F'r'instance。 –

答えて

1

これを理解するには、マシンコードを読んでください。ジョナサン・レフラー(Jonathan Leffler)が言ったように、これは未定義の行動です。

これはおそらく、コンパイラとメモリ内の内容をどのようにレイアウトするかによって異なります。サイズ7の配列が実際の7バイトを取得する必要はありません。それはより良いスタックレイアウトのために16にそれを埋め込むことを決定するかもしれません。または、別の位置に配置すると、その後ろにゼロがあり、余分なスペースがありますが、大きな配列には異なるプレースメントが割り当てられます。

または最適化を有効にしてコンパイルした場合、コンパイラはサイズ7のループを "正しい"コードにアンロールして、単純なputs("hello, world");になりますが、大きなループをインライン化できません。

私は好奇心のため少し実験を行いました。答えはLinuxのGCCで、答えは64ビットモードでは常に8バイトのchar *tのポインタ"world"に十分なスペースがあるためです。

32ビットモードでは、リターンスタックを上書きするため、説明どおりに失敗します。しかし、戻り値を過ぎるまではゼロを見つけられないので、7つの作業はゴミデータで並べ替えられ、スタックスタックの空き領域に「追加」されます。印刷結果がhello, ��#m�?��world

0

のように見える私はあなたが使用しているコンパイラ知らないが、Xcodeはchar配列が長すぎる

+0

私はgccコンパイラを使用しています – BridgeTheGap