みんなの「教えて(疑問・質問)」にみんなで「答える」Q&Aコミュニティ

こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

C言語のプログラムでおかしな動作をするのですが教えて頂けないでしょうか?

VisualStudio2008使用しています。
問題は、サイコロを200回振ってその出た目の数の個数分*を表示するプログラムです。
サイコロの目はランダムで出しています。
次のプログラムは正常に動作するものです。
/*
#include <stdio.h>
#include<stdlib.h>
#include<time.h>
#define N 200

int DICE(int min,int max);
int main()
{
int n,i,j;
int y[7]={0};
srand((unsigned int)time(NULL));

for(i=0;i<N;i++){
n=DICE(1,6);
y[n]++;
}

for(i=1;i<7;i++){
printf(" %2d: ",i);
for(j=0;j<y[i];j++){
printf("*");
}
printf("\n");
}

return 0;
}

int DICE(int min,int max)
{
return min+(int)(rand()*(max-min+1.0)/(1.0+RAND_MAX));
}
*/

次のプログラムが問題で、授業で先生が配列にはstaticをおまじないとしてつけないと暴走すると言われたので、つけて見ると明らかに間違ってると思われるプログラムで動作するのですが原因を教えて頂けないでしょうか?
以下問題のプログラム!
配列の前にstaticをつけたら、添え字をいくつにしても正常に動作します。普通は添え自分しか領域って確保されないですよね???
/*
#include <stdio.h>
#include<stdlib.h>
#include<time.h>
#define N 200

int DICE(int min,int max);
int main()
{
int n,i,j;
//以下が問題の配列宣言
static int y[2]={0};
srand((unsigned int)time(NULL));

for(i=0;i<N;i++){
n=DICE(1,6);
y[n]++;
}

for(i=1;i<7;i++){
printf(" %2d: ",i);
for(j=0;j<y[i];j++){
printf("*");
}
printf("\n");
}

return 0;
}

int DICE(int min,int max)
{
return min+(int)(rand()*(max-min+1.0)/(1.0+RAND_MAX));
}

質問の意味が正確に伝わらなかった場合は補足しますので、ご回答よろしくお願いします。

投稿日時 - 2009-06-09 13:32:36

QNo.5029554

すぐに回答ほしいです

質問者が選んだベストアンサー

ローカル変数の場合はスタックと呼ばれる領域に確保されます。
この場合、指定したデータ量分しかもらえません。
例えば char s[2] = { 0xDE, 0xAD};とした場合

┏━┳━┳━┳━
┃s0┃s1┃s2┃s3...
┣━╋━╋━╋━
┃DE┃AD┃プログラムに必要なデータ等等・・・

s[2](s2)以降に何かを書き込むとデータが上書きされプログラムが暴走します。

staticをローカル変数につけるとスタックとは別の領域に置かれます。
この領域はコンパイル時に自動的にある程度余裕を持って確保されます。
また、データのみの領域としてローカル変数のようにプログラムが使用するデータ(実行に必要なアドレスなど)はありません。
static char s[2] = { 0xDE, 0xAD};とした場合
┏━┳━┳━┳━
┃s0┃s1┃s2┃s3...
┣━╋━╋━╋━
┃DE┃AD┃空き領域
s[2] = 0xBE; s[3] = 0xEF;と代入すると
┏━┳━┳━┳━
┃s0┃s1┃s2┃s3...
┣━╋━╋━╋━
┃DE┃AD┃BE┃EF
と一見なんの問題もないようにデータが書き込まれます。
しかし、
static char s[2] = { 0xDE, 0xAD};
static char t[2] = { 0xCA, 0xFE};
として
┏━┳━┳━┳━
┃s0┃s1┃t0┃t1...
┣━╋━╋━╋━
┃DE┃AD┃CA┃FE
s[2] = 0xBE; s[3] = 0xEF;と代入すると
┏━┳━┳━┳━
┃s0┃s1┃t0┃t1...
┣━╋━╋━╋━
┃DE┃AD┃BE┃EF
のように上書きされてしまいます。
今回はstaticな変数が一つだったので問題がないように見えただけです。

つまり、staticをつけると普通のローカル変数とは記憶される場所が違うということです。

投稿日時 - 2009-06-09 15:02:16

お礼

回答ありがとうございます。

図示して頂きまして、視覚的に理解することが出来ました。
原因についてようやく理解が出来ました。
本当にありがとうございました。

投稿日時 - 2009-06-09 17:44:04

このQ&Aは役に立ちましたか?

1人が「このQ&Aが役に立った」と投票しています

回答(5)

ANo.5

「static をつけて正常に動作しているように見える理由」はたぶん #2 の通りでしょう. 実際には関係ないところをぶち壊してるんだけど, それがプログラムの動作にさえ関係ない, というだけ. ゴミだろうとなんだろうと「メモリはメモリ」なので, 当然値を変更すれば新しい値を保持することができます. 今の場合は乱数でなんかしているので, (乱数の初期化がきちんとできていれば) 実行ごとに結果が変わっても不思議ではありません.
しかし, 「配列にはstaticをおまじないとしてつけないと暴走する」て.... むしろ暴走するならその方が問題としては良性なんだけどなぁ. 「問題があるにもかかわらずないようにふるまって, あるとき突然噴出する」みたいに, そのうち呪いのつけが返ってくるかもしれん.

投稿日時 - 2009-06-09 15:15:47

お礼

回答ありがとうございます。

>static をつけて正常に動作しているように見える理由
なるほど!
分かりやすい説明ありがとうございました。

投稿日時 - 2009-06-09 17:30:31

ANo.3

確かに、staticをつけることによって暴走しなくなることはありえます。
しかし、staticをつけようがつけまいが、プログラムが間違っているということには違いがありません。
たまたまstaticをつけて暴走しなくなるからといって、それをわざわざ授業で取り上げる先生の意図がわかりません。ただの知識のひけらかしでしょうか。

実際のところ、なぜ暴走しなくなるかの説明はCPUの知識やらなにやらの知識が必要で、初心者が理解するのは大変です。そんなことを授業でとりあげても混乱をまねくばかりで、なにも得ることはないと思います。

投稿日時 - 2009-06-09 14:18:15

お礼

回答ありがとうございます。

staticはかなり奥が深いんですね。
ちなみに、今回のstaticをつけて実行したのはどのような原因が考えられるのでしょうか?
何回実行しても、値もちゃんと変わっていて見た目は正常に動作しているように思えます。
その辺りのご意見を頂けないでしょうか?

投稿日時 - 2009-06-09 14:35:05

ANo.2

staticの有無により「配列が、メモリ上のどこに配置されるか」が決まります。

staticが無い場合、関数内で宣言された配列は「スタック領域」に一時的に確保されます。

この「スタック領域」には「関数がどこから呼ばれ、どこに戻るのか」とか「一時的に退避してあって、後で元に戻す為の値」など、色々な情報が「隙間無くみっちり」と格納されています。

なので「配列の要素数を超えて書き込んだら、すぐ隣にある、とても重要な情報が壊されて暴走」します。例えば、関数から戻る時、全然違う場所に戻ったり。

staticがある場合、関数内で宣言された配列は「静的メモリ領域」に恒久的に確保されます。

通常、この「静的メモリ領域」は、ビルド時(コンパイル時)に「○○キロバイト確保しておいてね」と指示する(指示しない場合は初期値)ので、最低でも「キロバイト単位」の大きさがあります。

もし、staticで確保してある変数が他になければ、配列の後ろに「キロバイト単位で、未使用になっているメモリ領域が広がってる」場合が多いので、多少、要素数を超えて書き込んでも、暴走したりせず、平気で動きます。

但し、未使用と言えど、メモリを破壊しているのは間違いないので、根本的な解決にはなりません。

なお、staticで確保してある変数が他にもあった場合、その「他の変数」の中身が壊される場合があるので、場合によっては暴走します。

投稿日時 - 2009-06-09 14:17:35

お礼

回答ありがとうございます。

この場合は、
>「キロバイト単位で、未使用になっているメモリ領域が広がってる」場合が多いので・・・
未使用部分が使用されて、その部分に入ってるゴミが表示されたのでしょうか?
ですが、実行毎に値がちゃんと変わってるのはなぜなのでしょうか?

投稿日時 - 2009-06-09 14:31:45

ANo.1

>配列の前にstaticをつけたら、添え字をいくつにしても正常に動作します。

「いくつにしても」というのは、おそらく正確ではないです。

static int y[2]={0};
のように、本来確保すべき大きさに満たない領域では、
正しく動く「ように見えている」だけです。

投稿日時 - 2009-06-09 14:08:08

お礼

回答ありがとうございます。

>正しく動く「ように見えている」だけです

私もそうだと思って、何度も実行しました。
プロジェクトも作り変えましたし、変数も変えてみました。
再起動してみたり、違うパソコンで実行したり・・・
はたまた、VisualStudio2003でも実行しましたが、正常に動作しました。

これは全て、偶然的に『正しく動く「ように見えている」だけ』
なのでしょうか???
かなりやり方を変えているので、ここまで来ると偶然なのかと思ったりします。
その辺りを教えて頂けないでしょうか?
よろしくお願いします

投稿日時 - 2009-06-09 14:26:12

あなたにオススメの質問