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));
}
質問の意味が正確に伝わらなかった場合は補足しますので、ご回答よろしくお願いします。
No.4ベストアンサー
- 回答日時:
ローカル変数の場合はスタックと呼ばれる領域に確保されます。
この場合、指定したデータ量分しかもらえません。
例えば 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をつけると普通のローカル変数とは記憶される場所が違うということです。
回答ありがとうございます。
図示して頂きまして、視覚的に理解することが出来ました。
原因についてようやく理解が出来ました。
本当にありがとうございました。
No.5
- 回答日時:
「static をつけて正常に動作しているように見える理由」はたぶん #2 の通りでしょう. 実際には関係ないところをぶち壊してるんだけど, それがプログラムの動作にさえ関係ない, というだけ. ゴミだろうとなんだろうと「メモリはメモリ」なので, 当然値を変更すれば新しい値を保持することができます. 今の場合は乱数でなんかしているので, (乱数の初期化がきちんとできていれば) 実行ごとに結果が変わっても不思議ではありません.
しかし, 「配列にはstaticをおまじないとしてつけないと暴走する」て.... むしろ暴走するならその方が問題としては良性なんだけどなぁ. 「問題があるにもかかわらずないようにふるまって, あるとき突然噴出する」みたいに, そのうち呪いのつけが返ってくるかもしれん.
回答ありがとうございます。
>static をつけて正常に動作しているように見える理由
なるほど!
分かりやすい説明ありがとうございました。
No.3
- 回答日時:
確かに、staticをつけることによって暴走しなくなることはありえます。
しかし、staticをつけようがつけまいが、プログラムが間違っているということには違いがありません。
たまたまstaticをつけて暴走しなくなるからといって、それをわざわざ授業で取り上げる先生の意図がわかりません。ただの知識のひけらかしでしょうか。
実際のところ、なぜ暴走しなくなるかの説明はCPUの知識やらなにやらの知識が必要で、初心者が理解するのは大変です。そんなことを授業でとりあげても混乱をまねくばかりで、なにも得ることはないと思います。
回答ありがとうございます。
staticはかなり奥が深いんですね。
ちなみに、今回のstaticをつけて実行したのはどのような原因が考えられるのでしょうか?
何回実行しても、値もちゃんと変わっていて見た目は正常に動作しているように思えます。
その辺りのご意見を頂けないでしょうか?
No.2
- 回答日時:
staticの有無により「配列が、メモリ上のどこに配置されるか」が決まります。
staticが無い場合、関数内で宣言された配列は「スタック領域」に一時的に確保されます。
この「スタック領域」には「関数がどこから呼ばれ、どこに戻るのか」とか「一時的に退避してあって、後で元に戻す為の値」など、色々な情報が「隙間無くみっちり」と格納されています。
なので「配列の要素数を超えて書き込んだら、すぐ隣にある、とても重要な情報が壊されて暴走」します。例えば、関数から戻る時、全然違う場所に戻ったり。
staticがある場合、関数内で宣言された配列は「静的メモリ領域」に恒久的に確保されます。
通常、この「静的メモリ領域」は、ビルド時(コンパイル時)に「○○キロバイト確保しておいてね」と指示する(指示しない場合は初期値)ので、最低でも「キロバイト単位」の大きさがあります。
もし、staticで確保してある変数が他になければ、配列の後ろに「キロバイト単位で、未使用になっているメモリ領域が広がってる」場合が多いので、多少、要素数を超えて書き込んでも、暴走したりせず、平気で動きます。
但し、未使用と言えど、メモリを破壊しているのは間違いないので、根本的な解決にはなりません。
なお、staticで確保してある変数が他にもあった場合、その「他の変数」の中身が壊される場合があるので、場合によっては暴走します。
回答ありがとうございます。
この場合は、
>「キロバイト単位で、未使用になっているメモリ領域が広がってる」場合が多いので・・・
未使用部分が使用されて、その部分に入ってるゴミが表示されたのでしょうか?
ですが、実行毎に値がちゃんと変わってるのはなぜなのでしょうか?
No.1
- 回答日時:
>配列の前にstaticをつけたら、添え字をいくつにしても正常に動作します。
「いくつにしても」というのは、おそらく正確ではないです。
static int y[2]={0};
のように、本来確保すべき大きさに満たない領域では、
正しく動く「ように見えている」だけです。
回答ありがとうございます。
>正しく動く「ように見えている」だけです
私もそうだと思って、何度も実行しました。
プロジェクトも作り変えましたし、変数も変えてみました。
再起動してみたり、違うパソコンで実行したり・・・
はたまた、VisualStudio2003でも実行しましたが、正常に動作しました。
これは全て、偶然的に『正しく動く「ように見えている」だけ』
なのでしょうか???
かなりやり方を変えているので、ここまで来ると偶然なのかと思ったりします。
その辺りを教えて頂けないでしょうか?
よろしくお願いします
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# C言語のエラーについて 2 2022/07/11 13:56
- C言語・C++・C# カードシャッフルのブログラムを使ってc言語でブラックジャックをしたい 2 2022/04/12 15:13
- C言語・C++・C# プログラミングを教えて欲しいです。 配列aは、int a[9]={7,6,12,8,3,5,10,9 4 2022/12/19 23:27
- C言語・C++・C# このプログラミング誰か教えてくれませんか 1 2022/06/02 15:27
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- C言語・C++・C# 並列プログラミングのπ計算について 1 2022/07/16 22:30
- C言語・C++・C# c言語 プログラムのエラー 1 2023/02/11 20:31
- C言語・C++・C# C言語の課題が出たのですが自力でやっても分かりませんでした。 要素数がnであるint型の配列v2の並 3 2022/11/19 17:41
- C言語・C++・C# C言語プログラム変更 2 2022/12/21 15:03
- C言語・C++・C# C 言語の Gauss Jordan 法について 2 2022/12/28 11:16
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
c言語
-
関数から配列を返すには?
-
100の階乗のプログラムって
-
ループカウンタを使用せず、配...
-
要素数・要素の値が未定の配列...
-
コンボボックスでデフォルト値...
-
配列のアドレス部
-
RAW画像高速表示について
-
2番目の最大値を求める
-
define で 配列
-
配列の要素数に変数を入れたい...
-
C言語の課題が出たのですが自力...
-
背景差分法における正規化距離
-
.NET C++で、構造体の配列をnew...
-
配列の配列をmemcpyやmemcmpし...
-
[C++/CLI]マネージコードで配列...
-
円周率を求めるプログラム
-
シマウマの模様を変えるプログラム
-
C#で構造体の配列を持った構造...
-
C言語についてです 5人のテスト...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
関数から配列を返すには?
-
配列の要素数に変数を入れたい...
-
define で 配列
-
c言語
-
C#で構造体の配列を持った構造...
-
構造体のextern方法
-
C言語において、 配列要素をひ...
-
C#でのフィボナッチ数列
-
C言語の2次元配列 容量が大き...
-
C#で配列が空かを判定するには?
-
配列のアドレス部
-
char型配列をint型に代入するには
-
2番目の最大値を求める
-
C言語の課題が出たのですが自力...
-
C言語についてです 5人のテスト...
-
C言語から質問です。
-
C言語 ファイルの指定された行...
-
c言語 構造体
-
コンボボックスでデフォルト値...
-
MFCのCArrayを使った二次元配列
おすすめ情報