もうすぐC言語のテストがあるので適当に自分で問題を作って
プログラムを作る練習をしていたのですが配列の所でちょっと疑問に思いました。
問題
ひとつずつ数字を入力していき、それまでの数字の合計と平均を求めるプログラム。
0を入力するとプログラム終了(配列、ポインタ、関数を用いること)
#include <stdio.h>
int wa(int *a,int b);
main()
{
int a[10],ans,i=0,c;
double ave=0,j;
while(1)
{
scanf("%d",&a[i]);
c=a[i];
i++;
if(c==0){
exit(1);
}
ans=wa(&a[0],i);
printf("合計%d\n",ans);
j=i;
ans=wa(&a[0],i);
ave=ans/j;
printf("平均%lf\n",ave);
printf("計算回数%d回\n",i);
}
}
int wa(int *a,int b)
{
int ans=0,i;
for(i=0;i<b;i++){
ans+=*(a+i);
}
return ans;
}
このようなプログラムで一応自分の期待通りには動いてくれたのですが、
こういう「0」を入力しない限り終わらないプログラムのときに配列を利用すると
どれぐらい領域を取っておくかがわからないんですよ。
今回はa[10]としてますが、結局10しか確保してないから10回しか入力できないかな?
っと思って実行してみますが普通に10回以上でもエラーがでることもなく実行できるんですよね。
これはなぜでしょうか?
私の配列の考え方がまちがっているのでしょうか?
No.4ベストアンサー
- 回答日時:
#1です。
お礼にお書きになったプログラムですが、流れ的にはそんな感じでいいと思います。
ただし、実はrealloc()はかなり重い処理なので、頻繁に呼び出すのはおすすめしません。
ですので、たとえば256個ずつ増やしていく、というように、ある程度まとまった形で拡げていくのがベターです。
ですから、最初のメモリ確保は
#define BLOCKSIZE 256
int len = BLOCKSIZE ;
a = (int *)malloc( sizeof(int) * len ) ;
if(a==NULL){
printf("メモリ確保失敗\n");
}
とやったうえで、whileの最後は
if ( i > len ) {
len += BLOCKSIZE ;
a = (int *)realloc( a, sizeof(int) * len ) ;
if(a==NULL){
printf("メモリ再確保失敗\n");
}
}
というようにすればよいでしょう。
(realloc()の結果も検査しましょう)
なお、本当にはみ出してないか心配になる気持ちはよく分かりますが、こちらもやはり歯止めはありませんので、注意深くプログラムを書かなければいけないのは配列と同じです。どうしても心配ならデバッガを使って観察しながら確認しましょうね。
No.3
- 回答日時:
No.2の方が言われているように、配列宣言を大きくオーバーするとエラーがでるはずです。
エラーが出ないのは「たまたまうまくいった」だけですね(^^;
なので、配列宣言はNo.1の方の挙げられている2つが一般的だと思います。
静的と動的って言います。
大きな配列を用いるときは動的に確保します。
でも、100個,1000個,10000個ぐらいなら静的でも問題ないです。
こういうときは普通
#define MAX 10000
int wa[MAX];
という風に、あらかじめ大きめに宣言をしておいて用います。
もしdefineが分からなかったら、ちょっと調べてみて下さいね~。C関連のどの本にもサイトにも載ってると思います。
No.2
- 回答日時:
この場合、配列のa[10]へデータをセットしたときに、配列を突き破りますが、たまたま、エラーが発生しないだけです。
a[10]は、自動変数といって、スタック領域にに確保されますが、a[10]に相当する場所(アドレス)に、他の変数が、割り当てられている場合は、その変数に、値がセットされることになります。一般的に、a[N]として、変数を宣言した場合、a[N-1]までが、正しく動作する範囲になります。a[10]を越えた場合は、見かけ上正しく動作する場合もありますが、だめなときもあります。例として、以下のプログラムを実行してください。
例1
int a[10];
int i;
for (i=0;i<11;i++){
a[i] = i;
}
例2
int a[10];
int i;
for (i=0;i<1000000000;i++){
a[i] = i;
}
例1は、たぶん見かけ上、正常に終了するでしょう。
例2は、プログラムが異常終了するでしょう。
No.1
- 回答日時:
確かにエラーになりませんが、誤動作し、最悪の場合システムダウンの原因にすらなり得ます。
これはメモリリークといいます。C言語やアセンブラに特有のもので、他の言語では普通起こりません(起こらないように作ってある)。
どういうことかといいますと、Cの場合、配列の添字に範囲を限定する歯止めを設けてないんです。10個分の配列を定義しても、メモリは10個分しかとられないのに、歯止めがないので11個目以降も普通に使えてしまいます。ついでにいうと、実はマイナスも使えます(a[-1]など)。
ところが、11個目以降は実際は配列のために用意した場所ではなく、別のものが入っています。それは、別の変数だったり、プログラムの領域だったり、もしかしたらOSが使っているかも知れません。
そんなところを使っていくということは、知らないうちに別の変数を書き換えてしまったり、プログラムを壊してしまったり。。。
プログラムにこういう問題があると、ハッカーの侵入口にすらなることもあるくらいです。
ですから、配列は定義した範囲をはみ出さないよう、十分注意してプログラムを作らなければならないのです。
さて、どのくらい使うかわからない時はどうするか?
方法はふたつあります。
ひとつは、単純に、なるべく大きめにとっておいて、そのサイズを越えるデータは受け付けないようにしてしまう方法です。これは、簡単ですが、どうしても制限ができてしまいますし、制限を越えることが少なくなるように十分大きく配列をとるとメモリをたくさん使ってしまいます。
ふたつめの方法は、ポインタとmalloc(),realloc()ライブラリ関数を使う方法です。配列の代わりにある程度の大きさのメモリをmalloc()確保して、そこの場所をポインタ変数に入れて覚えておきます。もちろん大きさも変数に入れてとっておきます。そして、途中で足りなくなるようだったら、realloc()で広げることができます。もちろん、余るようなら減らすこともできます。
この方法だと、無駄なく効率よくメモリを使えますし、たくさん必要になってもメモリの許す限りいくらでも使えます。
一般的にはこちらの方法を使うケースが多いと思います。
まあ、これはちょっと難しいかも知れないので、今回は理屈のみの説明にしておきますね。
malloc()とrealloc()を使って考えてみました。
結局これも10以上になったとしてもエラーが出ないで動くのでこのプログラムが正しいかどうか
ちょっと疑問な気もしますがこんな感じでしょうか?
mainの変数宣言の下の行に
a=(int*)malloc(sizeof(int)*10);
if(a==NULL){
printf("メモリ確保失敗\n");
}
while(1)の最後の行に
if(i>9){
a=(int*)realloc(a,sizeof(int)*(i+1));
}
while(1)を抜けたあとに
free(a);
を追加して、元々10個とってあった領域が足りなくなったら1ずつ増やしていくというふうに
したつもりなのですが、これは確保した領域をはみだしてないですよね?
エラーにならないのでいまいち自信がもてないんですよね(^^;)
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# このプログラミングの問題を教えてほしいです。 キーボードからデータ数nとn個のデータを入力し、平均値 3 2022/12/19 22:51
- C言語・C++・C# このプログラミング誰か教えてくれませんか 1 2022/06/02 15:27
- C言語・C++・C# C言語 3 2022/10/04 15:07
- C言語・C++・C# プログラミングを教えて欲しいです。 配列aは、int a[9]={7,6,12,8,3,5,10,9 4 2022/12/19 23:27
- C言語・C++・C# C言語プログラム変更 2 2022/12/21 15:03
- C言語・C++・C# c言語 プログラムのエラー 1 2023/02/11 20:31
- Java javaでのプログラム(配列)について質問です. 2 2022/10/14 22:27
- C言語・C++・C# C言語階乗の総和を求める 2 2023/03/04 23:31
- C言語・C++・C# 10個の実数に対する降順ソート結果を出力するプログラムを作りたいのですが、以下のプログラムをどう直せ 1 2022/07/09 22:16
- C言語・C++・C# C言語の課題が出たのですが自力でやっても分かりませんでした。 要素数がnであるint型の配列v2の並 3 2022/11/19 17:41
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
関数から配列を返すには?
-
配列の要素数に変数を入れたい...
-
2番目の最大値を求める
-
配列の疑問。
-
Cのエラー
-
ポインタを使って構造体の配列...
-
ファイルのデータを構造体に代...
-
構造体のextern方法
-
【速いブラインドタッチ】手を...
-
VBAにてcolorindexを変数に格納...
-
日付チェック関数について
-
C言語 配列の長さの上限
-
C言語のintとcharの違いってな...
-
擬似コード 長さがmの配列でな...
-
CImage GetBitsメソッドについて
-
ヘッダファイルと構造体
-
callocの処理速度
-
mallocについて
-
【エクセルVBA】Functionの引数...
-
二次元配列の引数渡し
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
関数から配列を返すには?
-
C言語において、 配列要素をひ...
-
配列の要素数に変数を入れたい...
-
構造体のextern方法
-
define で 配列
-
c言語
-
C#で構造体の配列を持った構造...
-
C言語の2次元配列 容量が大き...
-
c言語 構造体
-
C言語 ファイルの指定された行...
-
C言語についてです 5人のテスト...
-
int i, int i[1];
-
fclose()でセグメンテーション違反
-
char型配列をint型に代入するには
-
C言語から質問です。
-
Cのエラー
-
コンボボックスでデフォルト値...
-
C言語の課題が出たのですが自力...
-
MFCのCArrayを使った二次元配列
-
[C++]const int と配列
おすすめ情報