lesson3 (update:2019/10/24) [ 例題3-a|例題3-b|例題3-c|例題3-d|例題3-e|課題3]
プログラムの処理の流れをコントロールする構文を制御構造といいます。 プログラムは通常、上から順番に実行されます。しかし、条件を見て実行する順番を変える ことができればとても便利です。
例えば:
a > b の場合と a <= b の場合で実行する
処理を変えることができます。
また、同じ処理を繰り返し何回も実行したい場合などは
繰り返し構文を使うと便利です。
命令文の実行順番を制御することを実行の制御と言いますが、 このページでは実行を制御する分岐と繰り返しの構文を紹介します。
目次
普通プログラムは上から下へ順番に処理されるので、その処理の流れは1本の道になっています。 しかし、条件に応じて処理を分けたい場合もあります。そこで、 処理の流れを分岐させるための構文としてif文が用意されています。
if(条件式) 文 ;
動作:条件式が真なら文を実行し、偽なら次の処理へ進む。
(注)文やの部分で複数の文を入れたい場合には複文(compound statement/ブロックblock)を使います。
if(条件式) {文1;文2;文3;文4;}
{ }で囲まれた部分が複文で、中には宣言の並び、文の並びの順番でプログラムを記述できます。
if文
以下に条件式で用いられる演算子を示す。大小関係を表す演算子を 関係演算子といい、等価関係を表す 演算子を等価演算子という。
|
一般形 |
意味 |
|
|
x<yなら真となり戻り値1、異なる場合は0 |
|
|
x≦yなら真となり戻り値1、異なる場合は0 |
|
|
x>yなら真となり戻り値1、異なる場合は0 |
|
|
x≧yなら真となり戻り値1、異なる場合は0 |
|
一般形 |
意味 |
|
|
等号: xとyの値が等しいとき真となり戻り値 |
|
|
不等号: xとyの値が等しくないとき真となり戻り値1 |
if(条件式) 文1 ;else 文2;
動作:条件式が真なら文1を実行し、偽なら文2を実行する。
if-else文
条件式の部分では「 a < b 」という条件式と「 c < d 」という条件式を 複数ならべて組み合わせることもできます。このように複数の条件式を組み合わせる 演算子を論理演算子という。 論理演算子には以下のようなものがあります。
論理演算子 |
一般形 |
意味 |
|
|
論理積:式Aと式Bがともに真なら戻り値1、異なる場合に0 |
|
|
論理和:式Aと式Bのいずれか真なら戻り値1、異なる場合に0 |
|
|
否定:式Aが偽なら戻り値1、式Aが真なら0 |
(注)条件の真偽を表わす専用のデータ型がC言語にはありません(Pascalやjava言語ならboolean型がある)。C言語では整数型の値で0の場合を偽、0以外を真とします。
入れ子のif文で多重分岐を記述できます。3個に分岐する場合は次の様に書きます。
if(条件式1)文1;else if(条件式2)文2;else 文3;これはif文のelse以下がさらにif文になった形です。上記の場合は条件に応じて文1〜3の何れか1つが必ず実行されます。
C言語のソースプログラムはフリーフォーマットと言って改行や複数の空白は命令の解釈においては1個の空白文字の意味しか持ちません。プログ ラマはプログラム構造が解かり易い様に自由に改行や空白を入れることができます。上記のif文も多くの場合下記の様に改行を入れて記述します。
if-else if-else文 最後のelse の代わりにelse if文を用いればいくらでも分岐の数を増やせます。
この他、多重分岐はswitch文もありますが、if else構文の様な汎用性がありません。これについては適当な参考書を見てください。
[目次]
繰り返しの構文にはwhile文、for文、do-while文などがあります。
プログラムの繰り返し実行ではどこで繰り返しを止めるのかの判定が重要です。while文、for文は繰り返し部分の先頭で条件判定、do-while文は繰り返しの最後で条件判定といった違いがあります。繰り返しの中で条件判定が必要になる場合もあるので後述するbreakなども用意されています。
もっとも一般的な繰り返しの構文はwhile文で
while(条件式) 文 ;
動作:条件式の値が0以外なら文を実行し再び条件式の判定を行う。結果的に条件式の値が 0になるまで文を繰り返し実行する。
while(条件式){文1; 文2;... }
上記の様に文を複文にすることでまとまったプログラムを繰り返し実行できる。
while文 while文の例(例題3-a 関数表)
#include<stdio.h>
#include<math.h>/*初等関数のライブラリを利用する為*/
int main(void)
{
double x=0.00;
while(x<=1.00){ printf("exp(%2.2f)=%10.8f\n", x,exp(x) ); x=x+0.05; } return 0; }(注)xを0から0.05づつ増やしたときに20回目に丁度1.00になるでしょうか? わずかでも計算誤差が有ると繰り返しがx=0.95で終わるかもしれません。
繰り返しの回数があらかじめ決まっている場合にはfor文を使います
for(初期設定式;条件式;再設定式) 文 ;
例: for (i=0;i<100;i++) { ……… }
動作:初期設定式を実行後、条件式の値が真(0以外)なら文を実行する。 再設定式を実行後、再び条件式の判定を行う。文を複文にすることでまとまった プログラムを繰り返し実行できる。
多くの場合次の様な形で使われる。ここでループを回る回数を数えるのに変数nを用いた。nは整数型なので浮動小数点数の様な計算誤差は発生しない。
for文の例int n;
double sum;
sum=0;
for(n=1; n<=100 ; n++ ){/*繰り返しの初期設定、条件、再設定がまとまっている*/
sum=sum+1.0/n;
}以下のようにwhile文で書き直すことも可能。
int n;
double sum;
sum=0;
n=1;/*初期設定式*/
while(n<=100/*条件式*/){
sum=sum+1.0/n;
n++;/*再設定式*/
}
例題3-b 関数表
#include<stdio.h>
#include<math.h>/*初等関数のライブラリを利用する為*/
int main(void)
{
double x=0.00;
int i;
for(i=0;i<=20;i++){/*i=0〜20で21回繰り返しが行われる*/
x=0.05*i; printf("exp(%2.2f)=%10.8f\n", x,exp(x) ); } return 0; }(注)せっかく繰り返し回数が明確な形にしたわけですから、ループ内で i の値 を参照するのはかまいませんが、変更するのはやめましょう。
繰り返しの文を実行後に条件判定をする場合の構文です。
do 文 ; while(条件式);
あるいは
do {文1;文2;...} while(条件式);
while文やfor文は繰り返しの文を実行する前に条件判定を行うのに対してこの構文は繰り返しの文の後で条件を判定します。
このため、条件式に依らずdoの後の文や複文は必ず1回は実行されます。
do-while文 | |
---|---|
[目次]
繰り返しの文の中では繰り返しから脱出する命令文:breakが使えます。if文と組み合わせて使うと繰り返しの条件判定を繰り返し の文の中で行えます。
break文の例題を示します。例題3-c 合計の計算
#include<stdio.h>
int main(void)
{
int n,x,sum;
sum=0;
while(1){/*無限ループ*/
/*関数scanf_sを用いて数値を読み込む*/
/*入力文字列を変換指定に従って変換して2番目以降の引数で 示されたメモリ番地に書き込む*/ /*例題では変換指定%dで10進数への変換を指定し、&xで変数xの メモリ番地を渡している*/ n=scanf_s("%d",&x);/*この関数は変換できたデータ数をnに戻す*/ /*旨く数値が読めればnは1のはず*/ if(n!=1)break;/*nが1以外なら、ループから脱出する*/ sum=sum+x; } printf("sum=%d",sum);/*合計を書き出す*/ return 0; } [実行結果] 12 <<整数を表す文字列を入力 34 43 a <<文字「a」を入力したので数値に変換できない。 sum=89 <<12+34+43の合計が出力された
これはループからの脱出ではなくて、whileやdo while文なら次の処理を条件式へ移す命令です。for文の場合は再設定式へ処理を移します。
breakが繰り返し文の外へのgotoであるのに対して、continueは繰り返し文の残りをスキップするgotoです。
breakやcontinueが用意されていますからプログラマがgoto文を使う必要性はほとんどありません。
例題3-d
ここでは1から100までの整数のうち偶数のみの和を求めるプログラムを示します。
#include<stdio.h> int main(void) { int i; int sum=0; for(i=1; i<=100; i++) { if(i%2==1) { continue; /*値が奇数の時は再設定式へ処理を移す*/ } sum += i; } printf("合計は %d\n",sum); return 0; }
goto文は次に実行する文をラベルで指定した位置に無条件で移す命令で、実行の制御にはif文とこのgoto文があれば十分です。
試しにi=0から9までiを増やしながら繰り返す処理を記述してみます。
i=0; LOOP:;/*ラベル*/
if(i<10){
printf("%d\n",i);
i=i+1;
goto LOOP;/*ラベルLOOPへ跳ぶ*/
}このプログラムは始めi=0として次のif文でi<10なら{ }内を実行します。この中でi=i+1でiを増やしてからgoto文でま たif文の前に戻っています。従ってi=0〜9で printf("%d\n",i);が繰り返し実行されます。
しかし、Cのプログラミングではこの様なif文とgoto文での実行の制御は推奨されてはいません。理由はif文とgoto文を多用したプログラムは複雑で間違え易いプログラムになりやすいからです。 このために繰り返しの構文としてfor文やwhile文が別途用意されています。
上の合計計算プログラムを使うとデータを入れているときに、どこまで入れたかわからなくなることが心配されます。そこで次が何番 目のデータ入力かプログラム側で示すことにします。この様にプログラムとユーザの間で交互にデータを出し合って実行されるプログラムを対話的といいます。
例題をscanf_sの前にprintfで何番目のデータ入力かプログラムで書き出す形に書換えてみます。
..
count=0; while(1){/*無限ループ*/ printf("%d番目を入力>",count+1); n=scanf_s("%d",&x);/*旨く数値が読めればnは1のはず*/ if(n!=1)break; sum=sum+x; count++; } ..
ここで、ちょっと注意してほしいことが有ります、それは、「printf関数は文字列を直ちに画面出力するわけではない」です。printf関数は標準出力:stdoutに書き出 しますが、画面などの実際の出力装置に書き出すのはOSの仕事です。
標準出力は、バッファリングと呼ばれる一時的にデータを貯える機能を持っています。これはデータを送る側と受けとる側でタイミン グを合せる為や、効率的なタイミングで書き出しを行う為に使われます。例えばprintfの書き出しも毎回行うのではなくて何回分かをまとめてから書き出 すほうが効率が良いと判断される場合があります。この様な判断は書き出す相手によって変化します。(注:Windowsのコマンドプロンプトでは毎回書き 出される)
しかし、対話的な動作を行う為にはこの様なバッファリングがじゃまになることが多いのです。そこで、対話的なプログラムでは溜め ている文字を書き出す指示を細めに行うことで、この問題を解決します。
溜めているものを直ちに書き出す指示fflush(stdout)をプログラムに加えると以下のようになります。
(メモ:バッファにたまっているデータを送り出す処理をフラッシュ:flush と言います)
#include<stdio.h> int main(void) { int n,sum,count; int x; sum=0; count=0; while(1){ printf("%d番目を入力>",count+1); fflush(stdout);//ここでstdoutに溜めている文字を書き出す n=scanf_s("%d",&x); if(n!=1)break;//数値に変換できない入力でループから抜ける sum=sum+x; count++; } printf("合計は%d",sum); return 0; }
[目次]
課題3 平均気温の計算
いくつかの気温のデータを入力し、平均気温を計算するプログラムを作りなさい。
気温を入れる変数を t:(double型)
気温データの合計を入れる変数を sum:(double型)
気温のデータの個数を入れる変数を num:(int型)
scanf_s関数の戻り値(scanf_s関数が読み込んだデータ数)を入れるための変数を n:(int型)
とする。このファイル(p03.c)を右クリック「対象をファイルに保存」で保存し、このファイルを使ってプログラムを作成せよ。
ヒント:
n:scanf_s()が読み込んだデータ数 t:気温を入れる変数(double型) /* 気温の読み込み */ while(1){ .... n=scanf_s("%lf",&t);/*正常な実数の入力ならデータが1個読めるはず*/ if(n!=1)break; .... }
提出締切:10月30日(水)
提出先:情報棟2F 就職室 レポートボックス