「キーボードから10個の実数を入力し、それらの平均を求めるプログラムを作れ。」という問題で私は、

#include <stdio.h>

void main(void)
{
float A;
float a,b,c,d,e,f,g,h,i,j;

printf("実数を入力してください:");
scanf("%f",&a);
scanf("%f",&b);
scanf("%f",&c);
scanf("%f",&d);
scanf("%f",&e);
scanf("%f",&f);
scanf("%f",&g);
scanf("%f",&h);
scanf("%f",&i);
scanf("%f",&j);
A=(a+b+c+d+e+f+g+h+i+j)/10;
printf("平均は%fです.\n",A);
}

と考えたのですが、もっとすっきりとしたプログラムはないのでしょうか?もっとシンプルにしたいのです。教えて下さい。よろしくお願いします。

このQ&Aに関連する最新のQ&A

A 回答 (3件)

同じ処理の繰り返しなのだから、


最低限必要な処理を考えて繰り返し文を使うと良いです

void main(void)
{
float sum = 0;
float data;
int i;

for (i = 0;i < 10;i++){
 printf("実数を入力してください:(%d/10)",i+1);
 scanf("%f",&data);
 sum+=data;
}
sum /= 10
printf("平均は%fです.\n",sum);
}

こんな感じでいかがでしょう
    • good
    • 0
この回答へのお礼

ありがとうございます。とても役に立ちます。

お礼日時:2001/07/13 14:00

※長いので、お暇な時にでも読んでください(^^;


※最終的なソースは、1番下にあります。

■1■変数の数を減らすことから始めよう

 今10個の値を入力するために、a~jの10個の変数を宣言していますが、もしこれが100個の値を入力する問題だったらどうしましょう?
 同じ用途の変数がたくさん必要な場合には、配列変数を使うと便利です。配列とは「1つの変数にたくさんの値を入れられるようにしたもの」です。配列の宣言は次のようにします。

    データ型 配列変数名[使う個数];

 例えばこの問題では、「 float a[10]; 」と宣言すれば、aの0番目( a[0] )から、aの9番目( a[9] )までが使えるようになります。(この a[0] ~ a[9] を、配列aの「要素」といいます。)
 宣言では[ ]内に使う個数を書き、使う時は0からの通し番号を入れるわけです。0から数えるので、a[10]は使えないことに注意してください。

 配列を使うと、次のように書き換えることが出来ます。(main関数内のみ)

    float A, a[10];
    
    printf("実数を入力してください:");
    scanf("%f",&a[0]);
    scanf("%f",&a[1]);
    scanf("%f",&a[2]);
    scanf("%f",&a[3]);
    scanf("%f",&a[4]);
    scanf("%f",&a[5]);
    scanf("%f",&a[6]);
    scanf("%f",&a[7]);
    scanf("%f",&a[8]);
    scanf("%f",&a[9]);
    A=(a[0]+a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8]+a[9])/10;
    printf("平均は%fです.\n",A);

■2■配列の各要素を、変数で使い分けよう

 上記プログラムは、全然すっきりなんてしていませんね。結局100個の値が必要ななったら、えらい手間がかかってしまいます。
 配列を使う一番のメリットは、各要素を変数で使い分けることができる点です。つまり、a[0] の代わりに、b = 0; a[b] と書くことができるのです。この変数bを、forループを使って1ずつ増やしてやれば、たとえ100個の値が必要になっても問題ないわけです。

 これを使って、前半部分は次のように書き換えられます。

    int i;    /* forループでは i を使うのが習慣です。 */
    float A, a[10];
    
    printf("実数を入力してください:");
    for (i = 0; i <= 9; i++) {
      scanf("%f",&a[i]);
    }

※forループの使い方が分からなければ質問してください。

■3■少しずつ計算しよう

 次に合計を求めて個数で割る部分についてですが、足し算を全部書いたらすごく大変ですよね?
 ここでは「足し込み」という、ちょっとしたテクニックを使って、プログラムを簡単にします。

 まず次のプログラムは理解できますか?
    int a = 1; printf("a=%d\n", a);  /* 当たり前ですが「a=1」ですね */
    a = a + 2;  printf("a=%d\n", a);  /* a + 2 が計算されて、a に代入されているので「a=3」です */

 この「ある変数に数を足した結果を、その変数にする」というのが「足し込み」です。普通は「 a = a + 2; 」ではなく、「 a += 2; 」のように省略形を使って書きます。
 この手法を用いて、a[0] ~ a[9] を、A に足し込みます。前もって A の値を 0 にしておけば、a[0] ~ a[9] の合計が求まるわけです。

 それではループ・足し込みを使って、後半部分を書き換えてみます。

    A = 0.0;
    for (i = 0; i <= 9; i++) {
      A += a[i];
    }
    A = A / 10;
    printf("平均は%fです.\n",A);

■4■同じループができちゃった

 前半と後半のプログラムを見比べてみると、全く同じ形の for文 がありますよね。前半では10回分の入力を、後半では10回分の足し算を行っているわけです。

 ここでちょっと考えてみてください。

 ここまででは「全部入力した後、全部計算する」という形になっているわけですが、「1個入力したら、1個足し込む」をループさせても、うまくいくような気がしませんか?
 実際にくっつけてみると、下記のようなソースになります。

    int i;
    float A, a[10];
    
    printf("実数を入力してください:");
    A = 0.0;    /* 後半ループの前に行う必要があったので、くっつけたループの上に持ってきます */
    for (i = 0; i <= 9; i++) {
      scanf("%f",&a[i]);
      A += a[i];
    }
    A = A / 10;
    printf("平均は%fです.\n",A);

 だいぶすっきりしましたよね?

■5■入力した1つ1つの値って、後で使いますか?

 配列を使って10個分の入力をしたわけですが、おかげで「何番目に何を入力したか」がいつでも分かって便利ですね。何せ a[ 何番目 ] と書くだけですから。(0番目から数えます。)

 ところで、この「1つ1つの入力した値」って、何かに使いますか?もし使わないのなら、全部取っておく必要ないですよね?
 そもそも配列にした理由は、「たくさんの入力を行うのに、別々の変数を用意するのが大変だったから」です。
 今はループを使って、「1個入力したら1個足し込み」にしているので、実は変数は1個でも構わないんです。だって入力した値を A に足し込んじゃえば、入力した値はもうどうでも良いわけで、その変数を何に使おうが問題ないわけですから。

 では、a[x] と書かれた部分を、a にしちゃいましょう。宣言とループ内の3箇所を直せばいいですね。

■6■掛け持ち変数は、バグの元!

 ところで変数 A って、何用の変数ですか?合計を求めるのに使って、その後平均を求めて・・・

 まぁ今回はその程度だから問題はあまり無いんですが、1つの変数を用途を限定せずにあちこちで使いまわすと、思わぬバグを引き起こすことがあります。
 ですので、合計用と平均用で変数を分けておきましょう。
 その際、用途が分かるような名前を付けたほうが、後で見た時&他の人が見た時に、理解しやすいコードになります。コメントをつけるのも良いことなのですが、変数1つ1つに、しかも出てくるトコ全部に書くわけにいかないでしょ?

 合計には sum または goukei 、平均には ave または heikin 等がよいでしょうね。

■7■マジックナンバーをなくそう

 「マジックナンバー」とは、プログラム中に出てくる意味不明な数字のことです。
 ここでは forループで使ってる 9 と、平均を求める際の 10 で、両方とも「10個のデータ」という問題絡みのものです。

 何が問題かというと、例えば問題を「50個の入力値の平均を求めるプログラム」に変えられた時、ぱっと見てどこを直せばよいかが分かりにくく、修正漏れによるバグを起こしやすいことです。

 このプログラムの場合、処理するデータの個数を「グローバル変数」や「マクロ」なりに置き換えて、修正個所を1つにしてしまいましょう。ただの「数字」が、変数などの「名前」になることで、プログラムも分かりやすいものになります。

※グローバル変数やマクロについて分からなければ質問してください。

■8■最終的なソース

    #include <stdio.h>

    int data_num = 10;    /* データの個数を変更する時は、ここを直せばOK */

    void main( void )
    {
      int i;
      float sum, ave, a;
      
      printf("実数を入力してください:");
      sum = 0.0;
      for (i = 0; i <= data_num-1; i++) {
        scanf("%f",&a);
        sum += a;
      }
      ave = sum / data_num;
      printf("平均は%fです.\n", ave);
    }

それでは頑張って勉強してくださいね
    • good
    • 0
この回答へのお礼

ありがとうございました。これから役立ちそうです。

お礼日時:2001/07/13 13:59

for文とsum +=x(sum = sum+x)を使ってみました。


少しはCらしくなったでしょうか?

void main(void)
{
float sum=0.0;
float Ave,x;
int i;
 for ( i=0 ;i<10 ;i++){
   scanf("%f",&x);
   sum += x;
 }
Ave = sum/10.0;
}
    • good
    • 0
この回答へのお礼

ありがとうございます。思わずみんなでなるほど~と感心してしまいました。

お礼日時:2001/07/13 14:02

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aと関連する良く見られている質問

Qscanf("%s", &gakusei[i].shimei);&以降のコラボが?

プログラムは以下です。
#include <stdio.h>
#include <string.h>

/* 学生の人数 */
#define GAKUSEI_NUM 3

/* 構造体の定義 */
struct TestKekka
{
char shimei[80];
int kokugo;
int sugaku;
int eigo;
};
int main(int argc, char* argv[])
{
int i,heikin;
/* 構造体の配列の宣言 */
struct TestKekka gakusei[GAKUSEI_NUM];

/* 学生のデータ入力する */
for (i = 0; i < GAKUSEI_NUM; i++)
{
printf("氏名:");
scanf("%s", &gakusei[i].shimei);
printf("国語の得点:");
scanf("%d", &gakusei[i].kokugo);
printf("数学の得点:");
scanf("%d", &gakusei[i].sugaku);
printf("英語の得点:");
scanf("%d", &gakusei[i].eigo);
printf("---------------\n");
}
------------------------------------------------------------------------------------------
 プログラムは以上ですが、

/* 学生のデータ入力する */
for (i = 0; i < GAKUSEI_NUM; i++)
{
printf("氏名:");
scanf("%s", &gakusei[i].shimei);
------------------------------------------------------------------------------------------
以上のscanf("%s", &gakusei[i].shimei);の&gakusei[i].shimeiの部分がよくわからないのですが!?

&はアドレス
gakusei は 配列変数名
[i]はint型
.は構造体メンバー演算子でshimeiを結び付けている?ここもいまいちですが!?
 
 以上のコラボレーションをうまくご説明がしていただけるお方がおられましたら、
 ご教授のほどをよろしくお願いいたします。

プログラムは以下です。
#include <stdio.h>
#include <string.h>

/* 学生の人数 */
#define GAKUSEI_NUM 3

/* 構造体の定義 */
struct TestKekka
{
char shimei[80];
int kokugo;
int sugaku;
int eigo;
};
int main(int argc, char* argv[])
{
int i,heikin;
/* 構造体の配列の宣言 */
struct TestKekka gakusei[GAKUSEI_NUM];

/* 学生のデータ入力する */
for (i = 0; i < GAKUSEI_NUM; i++)
{
printf("氏名:");
scanf("%s", &gakusei[i].shimei);
printf("国語の得点:");
scanf...続きを読む

Aベストアンサー

> &gakusei[i].shimeiの部分がよくわからないのですが!?


1. gakuseiは、タグ名TestKekka構造体の配列(要素数は、0~2の3個)
2. タグ名TestKekaは、shimei[80],kokugo,sugaku,eigoのメンバ変数で構成されている。
3. 構造体のメンバ shimeiは、char型変数の配列(要素数は、0~79の80個)

4. gakusei[i]は、gakusei[0]~gakusei[2]のいずれか
5. gakusei[i].shiemeiは、gausei[i]のメンバshimei[80]の先頭アドレス


 以上は、メモリ上に下記の様に配置されています(※1)
 変数i は0~2のいずれかなので、
 gakusei[i].shiemeiは下記右側の矢印部分のいずれかになります。
 (既に回答がついている様に、shimei[80]は配列なので
   先頭アドレスは&が付かないgakusei[i].shimeiになります。)

――――――――― shimei[ 0](char型) ←ここがgakusei[0].shimeiの場所(アドレス)
    ↑     shimei[ 1](char型)
 この範囲が     ……
  gakusei[0]   shimei[79](char型)
    │     kokugo(int型)
    ↓     sugaku(int型)
――――――――― eigo(int型)
――――――――― shimei[ 0](char型) ←ここがgakusei[1].shimeiの場所(アドレス)
    ↑     shimei[ 1](char型)
 この範囲が     ……
  gakusei[1]   shimei[79](char型)
    │     kokugo(int型)
    ↓     sugaku(int型)
――――――――― eigo(int型)
――――――――― shimei[ 0](char型) ←ここがgakusei[2].shimeiの場所(アドレス)
    ↑     shimei[ 1](char型)
 この範囲が     ……
  gakusei[2]   shimei[79](char型)
    │     kokugo(int型)
    ↓     sugaku(int型)
――――――――― eigo(int型)


 (※1)正確には異なる可能性がありますが、今回の説明では問題ないと考えます。

> &gakusei[i].shimeiの部分がよくわからないのですが!?


1. gakuseiは、タグ名TestKekka構造体の配列(要素数は、0~2の3個)
2. タグ名TestKekaは、shimei[80],kokugo,sugaku,eigoのメンバ変数で構成されている。
3. 構造体のメンバ shimeiは、char型変数の配列(要素数は、0~79の80個)

4. gakusei[i]は、gakusei[0]~gakusei[2]のいずれか
5. gakusei[i].shiemeiは、gausei[i]のメンバshimei[80]の先頭アドレス


 以上は、メモリ上に下記の様に配置されています(※1)
 変数i は0~2のいずれかなので、
...続きを読む

Qprintf( "%d", i % 10 );で?

int count;
int i;
scanf( "%d", &count );
for( i = 0 ; i < count ; i++ )
printf( "%d", i % 10 );「iを10で割った余り」だそうです。
i%5とした場合、 
 仮に5と入力すれば、01234と表示すると思いますが、
何でiを5で割れば5進数みたいにコンピュータが認識するのですか?
理論だけ勉強中で、実際試したことがありません? 
よろしくお願いします。

Aベストアンサー

例えば、246を例に考えてみます。

246を5で割ると49で余りが1

これは書き換えると
246 = 49x5 + 1 という事ですね。

次に49について同様に行なうと
49を5で割ると9で余りが4

これは書き換えると
49 = 9x5 + 4という事ですね。

最初の結果とあわせると、
246 = 9(x5x5) + 4(x5) + 1
という事ですね。

同様に9についても計算すると

246 = 1(x5x5x5) + 4(x5x5) + 4(x5) + 1

となります。


5で割った答えと5で割った余りは、5進数で一つ上の桁へ移せる部分とその桁に残る部分を分けている事になります。
10進数でも32を考えた時、30の部分は上の桁に移せる部分で(10で割った答え部分)2はその桁に残る部分(2 = 32 % 10)ですよね。

Qscanf("%s", buf);でスペースを含んだ文字

コンソールプログラムで
scanf("%s", buf);
を使用してユーザに入力された文字によって処理を行いたいのですが、このままではスペースを含む文字列がスペースの手前で切られてしまいます。
C:\Program Filesなどを入力可能にさせたい場合にはどのようにするのがベターですか?

Aベストアンサー

お任せください!
そもそもscanfを使うというのはお勧めでは
ありません。scanfは文字+改行文字が入力
されないと完了しないためです。
が、それは良しとしましょう。
scanfの書式ですが、

int n = scanf("%[^\r\n]",buf);

という便利な書式があります。
perlでもおなじみの書式ですね。
上記の山文字"^"より前が読み込ませたい文字の集まりで、ハイフン指定が出来ます。
"^"より後ろが読込みを停止させたい文字の集まりです。上記の指定は復帰改行以外の文字が現れるまで読み込みます、という書式です。
下記のような指定も出来ます。

int n = scanf("%[a-zA-Z0-9\\: \t^\r\n]",buf);

なお、戻り値は読み込んだ項目数ですので、
if(n >= 1)
{
}
で判断できますね。

Qprintf("%2.d",0);は?

printf("%2.d",0);
を表示すると何も表示されません・・。
どうしたらいいのでしょうか?

Aベストアンサー

「%2.d」は「%2.0d」と同じです。

つまり「2文字の幅で、少なくとも0ケタの数字を書け」です。

この「少なくとも0ケタの数字を書け」は、言い替えれば「先頭にあるゼロは、全体が0ケタになるまで削ってよし」と言う意味です。

結果「_0」('_'は半角スペースの意味)の先頭のゼロを0ケタになるまで削るので「__」になります。

ゼロの時に「_0」と表示したいなら「%2d」と指定しましょう。

Qvoid (*signal(int signum, void (*handler)(int)))(int);

の解釈を教えてください
最後の「(int)」については詳しくお願いします

Aベストアンサー

signalが

(1)1つ目の引数の型:int
(2)2つ目の引数の型:引数がintで戻り値がvoidである関数へのポインタ
(3)戻り値の型:引数がintで戻り値がvoidである関数へのポインタ(2と同じ)

を満たす関数である事を宣言しています。最後の(int)はsignalの戻り値の
関数ポインタがint型の引数を持つ事を示しています。

「引数がintで戻り値がvoidである関数へのポインタ」の型をHANDLERと表すと

HANDLER signal(int signum, HANDLER handler);

となります。


人気Q&Aランキング

おすすめ情報