2009-04-27 11 views
5
申し訳ありません

これはnoobの質問:(Cコードの配列内に要素が多すぎます! 。。

ワンピースであれば

int array[5]; 
int cnt; 

for(cnt = 0; cnt <= 10; cnt+=1) 
{ 
     array[cnt] = cnt; 
} 

は右、エラーを与えるべきではありません罰金作品 しかし、なぜそれがあるそれはそれです!?!? - 最初の行ではダブル・サイズ(11)以上の配列が定義されていますが、後で配列[5~10]にアクセスすることもできますが、混乱します。 ...

ありがとうございます。

答えて

23

あなたの特定のコンパイラとコンピュータでうまくいくかもしれませんが、それにはカウントしてはいけません。

C言語仕様に準拠したコードの動作は、未定義です。これは、あなたが望むことをする可能性があること、またはコンピュータがクラッシュする可能性があること、またはdemons to fly out your noseを引き起こす可能性があることを意味します。

JavaやC#などの高級言語とは異なり、Cは信頼して、配列の境界を明示的にチェックしません。あなたは責任を負うべきであり、配列の境界の外側を踏まないでください。

+3

+1鼻の悪魔defenstrationのために! – RBerteig

+2

@RBerteig:私は "逸脱"(defenstrationは単語ではない)があなたがそれが意味すると思うことを意味するとは思わない。英語では、誰かを窓越しに投げ込む行為になります。 http://en.wikipedia.org/wiki/Defenestration –

+0

実際にはタイプミスですが、選択肢は意図的でした。私は予期せぬ追放感の正しい量を持った「悪魔」でいっぱいになったものに手を伸ばしていました。 – RBerteig

4

"なぜですか?"

これはCの方法です。

実行時に配列の境界がチェックされません。 「作品」のあなたの定義は、「まだクラッシュしていない」と同義である場合

「Cの法則」だ

16

これだけは「作品」。

+2

+1:私の次のコードレビューのためにその行を "借りる"ことがあるかもしれません;-) – RBerteig

+1

もう1つありますか"作品"の定義? – spiderman

+0

なんて素晴らしい謝辞、ありがとう! – Charlie

0

アレイの終わりを逃げたら、ソフトウェアが期待していないメモリを上書きして、ヒープを破壊します。あなたはソフトウェアを実行し続けるかもしれませんが、それは非常に不安定になります!

0

スタックメモリのパック方法によって異なります。また、これらの値をうれしく上書きして読み込むこともできますが、おそらくスタックを破壊している可能性があります。

5

あなたが見ているのは、無効なインデックスを持つ配列にアクセスしたために発生するの未定義の動作です。未定義の動作とは、プログラムが正しく動作するように見えるなど、何かが起こる可能性があることを意味します。

2

Cの配列は実行時にチェックされません。言い換えれば、サイズNの配列を定義して、配列の最後から幸せにアクセスできます。配列の終わりに行くと、スタック(またはヒープ)のどこかでメモリがゴミ箱になります。

一度どこかのメモリをゴミ箱に入れたら、プログラムがクラッシュする可能性があります。これらのクラッシュは、実際にアレイの終わりにオーバーランする場所から遠く離れてクラッシュする可能性があるため、追跡が難しい場合があります。

あなたはCで配列を宣言する場合、通常、それは配列のサイズをマークするために、一定またはの#defineのいくつかの並べ替えを使用するのが最善です:

#define MAX_ELEMENTS 10 
int array[MAX_ELEMENTS]; 
int cnt; 
for(cnt = 0; cnt < MAX_ELEMENTS; cnt+=1) { 
    array[cnt] = cnt; 
} 

あなたは、配列の割り当てにMAX_ELEMENTS過ぎて行く場合は、あなたがかもしれませんcntの値を上書きします。他の変数を上書きするかもしれません。すべては、コンパイラとコード構造に依存します。また、forループの記号<の使用にも注意してください。 C配列は0ベースなので、より小さい、より小さい、またはより小さいを使用してチェックする必要があります。

4

私はこれが本当に未定義であることを指摘したいと思います。 この例では、両方の変数がスタックに配置されているため、この例では「動作します。つまり、cntのアドレスは配列の終わりのすぐ下にあります。 cntがcnt == 5に達すると、 ステートメント配列[cnt] = cnt;配列専用のメモリには書き込まれませんが、その直後にはcntのアドレスが書き込まれます。それはあなたのカウンターを変更しないだけ運がいいです。 cnt> 5のときは、ゴミ箱のメモリがなく、 "スタックボイド"(適切な単語が分からない)に書き込むだけです。

本説明する別の例:

int main(int ac,char **av) 
{ 
    int a[5]; 
    int cnt; 
    int cnt2=3; 

    for(cnt=0;cnt<7;cnt++) { 
     a[cnt]=cnt; 
     printf("%d %d %d\n", a[cnt], cnt, cnt2); 
    } 
} 

出力:

0 0 3 
1 1 3 
2 2 3 
3 3 3 
4 4 3 
5 5 5 
6 6 5 

ループの最後の2つの書き込み[]は後のスタックデータが上書きされ、非常に混乱エラーをもたらす可能性があります。この場合、cnt2は廃棄されます。

+0

いいえイラスト。残念ながら、私はここで2つの最善の答えをマークすることはできません。 :\ – spiderman

1

Cの配列境界は、実行時にであると必ずしも確認されていないです。標準によって、実装者は自由に選択することができます。そうでなければ、それはの一部であり、定義されていないのはです。ファットポインタを使用した実装では、実際にサンプルが何らかのエラーを引き起こす可能性があります。