C言語/制御文

出典: フリー教科書『ウィキブックス(Wikibooks)』
ナビゲーションに移動 検索に移動

条件分岐[編集]

if文[編集]

具体例[編集]

「もし明日が晴れだったら、出かけよう。晴れでなかったら、出かけないでいよう。」のように、何かの条件が満たされたどうかで結果が変わることを条件分岐という。

C言語では条件分岐には、if文(イフぶん)を用いる。

C言語では、「もし変数aが4だったら、変数gには15を代入しなさい。」のように、条件分岐は命令として扱われる。

なお、プログラムでは「もし変数aが4だったら、変数gには15を代入しなさい」は、

if (a==4)
    g=15;

のように記述される。


条件が満たされる場合のことを(しん)という。条件が満たされない場合のことを(ぎ)という。

if (a==4)

と書かれた場合、もしaが4なら条件が満たされているので、「真」である。もしaが7なら、条件が満たされてないので、「偽」である。


#include <stdio.h>

int main(void)
{
  int a=4;
  if (a==4)    {
      printf("aは4である。\n");
      int g=15;
      printf("gは%dである。\n",g);
  }

    return 0;
}
実行結果
aは4である。
gは15である。


このように、if文の条件式(上記のプログラムの場合は a==4)が満たされるときには中カッコ{}の内部が実行される。

なお、条件式を書くときの等号は、==になる。条件式における等号は、けっして=ではない。(C言語では、ひとつだけのイコール記号=は代入記号の意味である。)


もし、条件式を変更すると、・・・

#include <stdio.h>

int main(void)
{
  int a=4;
  if (a==6)    {
      printf("aは4である。\n");
      int g=15;
      printf("gは%dである。\n",g);
  }

    return 0;
}
実行結果

なにも表示されずに終了する。条件式が偽なので、if文の{}内部を実行しない。


よくある間違い[編集]

  • ==演算子と=演算子の誤用

if文でよくある間違いの1つに、 ==演算子の代わりに=演算子を誤って用いることがある。 =演算子は左辺に右辺を代入した後、その値を返すため、 右辺が0の場合は偽となり、右辺が0以外の場合は真となる。 コンパイルエラーとはならないが、予期しない結果を引き起こす。

if (i=0)
	printf("iは0");

上の例では、 式「i=0」の評価結果はiの値によらず偽であり、 printf関数の文は常に実行されない。

  • 誤った;の付加

if文でよくある間違いの1つに、 if文の後に誤った;を付加することがある。

if (i==0);
	printf("iは0");

上の例では、 if文の式が真の時に実行される文がなく、 printf関数の文は常に実行される。


if文の書式[編集]

if文には次の2通りの形式がある。

  • if形式
if ()
	

if形式では、条件式が真の場合には文を実行し、 偽の場合には文を実行せずif文の次の文に制御を移す。

  • if-else形式
if ()
	
else
	

if-else形式では、制御式の論理値が真の場合は最初の文だけを実行し、 偽の場合は2番目の文だけを実行する。 ラベルを通して最初の文に制御が到達した場合、2番目の文は実行しない。

  • if-else if-...-else形式

上の2つの形式を組み合わせて次のように連続して記述することもできる。

if ()
	
else if
	
.
.
.
else
	

elseは上の2つの形式で許されるifのうち、そのelseの前で最も近い位置にあるifと対応する。

なおif文に限らないが、一般に文はブロックであってもかまわない。

//例 if文の使用例
#include<stdio.h>

int main(void)
{
	printf("数値を入力してください。:");
	int i;
	scanf("%d", &i);
	if(i==0)
		printf("入力値は0");
	else if(i==1)
		printf("入力値は1");
	else
		printf("入力値は0,1以外");
}

if文とは、選択文の内の1つであり、式(制御式)の論理値に従って、指定する文(副文)を実行するかどうか選択する。 if文には、if形式、if-else形式の2通りの形式があり、またそれらを組み合わせてもよい。

なお、WindowsのVisual C系のコンパイラではscanfが使えなく、上記のコードのままではエラーになるので、かわりにscanf_sという命令を使用します。

//例 if文の使用例
// Visual C 用のコード

#include<stdio.h>
#include <stdlib.h> // system関数で使用

int main(void)
{
	printf("数値を入力してください。:");
	int i;
	scanf_s("%d", &i,10);
	if (i == 0)
		printf("入力値は0\n");
	else if (i == 1)
		printf("入力値は1\n");
	else
		printf("入力値は0,1以外\n");

	system("pause"); // 「続行するには何かキーを押してください . . .」を表示する。

	return 0;
}

scanf_s の3つ目の引数(「10」)は、入力するデータサイズの限度です。


  • 参考文献サイト
Yahoo知恵袋『c言語実行画面がすぐ消えてしまう 僕はvisual stu』 [1] 2018年7月2日に閲覧

(※ ウィンドウズのVisual C では、コード実行終了後にウィンドウがすぐ消えてしまうのが標準設定になっている。なのでsystem("pause")などを使う必要がある。これは標準入出力には入ってないので、#include <stdlib.h>でインクルードしないといけない。)

if文の書き換え[編集]

if文は、0が偽、0以外が真であることを用いて、次のように簡潔に書き換えることができる。

if(i!=0)
//↓
if(i)

if(i==0)
//↓
if(!i)

if文のネスト[編集]

if文の中にif文を入れることを、ネストまたは入れ子という。

//例 if文のネストの使用例
#include "stdio.h"

int main(void)
{
	printf("数値を入力してください。:");
	int i;
	scanf("%d", &i);
	if(0 <= i)
		if(i <= 10)
			printf("入力値は0以上10以下");
}

WindowsのVisual C系コンパイラでは、scanfが利用できないので上記のコードはエラーになり利用できません。Visual C では、代わりに scanf_s という命令を使用します。

//例 if文のネストの使用例
// Visual C 用
#include "stdio.h"
#include <stdlib.h> 

int main(void)
{
	printf("数値を入力してください。:");
	int i;
	scanf_s("%d", &i, 100);
	if (0 <= i)
		if (i <= 10)
			printf("入力値は0以上10以下\n");

	system("pause"); // 「続行するには何かキーを押してください . . .」を表示する。

	return 0;
}

なお、上の(非Visual C系のコードの)例は &&演算子 を用いて、次のように簡潔に書き換えることができる。

// 例 if文で&&演算子を用いる。
#include "stdio.h"

int main(void)
{
	printf("数値を入力してください。:");
	int i;
	scanf("%d", &i);
	if(0 <= i && i <= 10)
		printf("入力値は0以上10以下");
}


switch文[編集]

[1]

switch文とbreak文とを使って、場合分け(複数個の「場合」のある場合)をできます。

たとえば

a=1なら、命令m1を実行
a=2なら、命令m2を実行
a=3なら、命令m3を実行
a=4なら、命令m4を実行

(以下略)

のような場合に、switch文とbreak文で、場合分けをできます。

コードはイメージ的に言うと、

(イメージ)

switch(a)
 case 1: 命令m1 ; break ;
 case 2: 命令m2 ; break ;
 case 3: 命令m3 ; break ;
 case 4: 命令m4 ; break ;

(以下略)

みたいな記法になります。

まず、実行したい命令の前に、それぞれcaseというラベルをつけます。

パソコンがswitch文に遭遇したときに、どのcaseに移動するかを決めます。

「case」ってのは、単に移動先を示している目印です。


C言語の仕組みとして、もしbreakがないとそのまま次の文を実行します。

つまり、

switch

case 1: 命令m1 ;
case 2: 命令m2 ;
case 3: 命令m3 ;
case 4: 命令m4 ;

(以下略)

というコードなら、もしa=2なら、実行されるのは、命令m2だけでなく、さらに命令m3や命令m4も実行してしまいます。

このような仕組み(つまりbreak文がないかぎり、次の命令を実行してしまう)のことをフォールスルーと呼びます。


このようなフォールスルー的な動作を行うことには、けっして、なんらかの深い工学的意味がありはしません。

単に、昔のC言語の開発者が、こういう仕組みで作ったままのものが、いまだに残っているだけです。

ですから、ほかのプログラム言語では、switch文においてフォールスルー的な動作が廃止されている場合もあります。たとえばグーグルの開発した「Go言語」というプログラム言語では、原則的にフォールスルーは行わないようになっており、例外的に現在実行中のcaseの下にある次のcaseを実行したい場合にだけ「fallthrough」という命令を付け加える、という仕組みです。(もちろんC言語には「fallthrough」命令はない。)

ともかく、C言語のフォールスルーを原則とする仕組みは、単なるローカル・ルールですので、悩む必要はありません。


また、このフォールスルーの動作からも分かるように、C言語における「case」は、単に移動先を示しているだけの目印です。単なるラベルです。caseの動作実態は「case」という名前とは違って、case文だけでは条件分岐をしてくれません。

break文などを補うことにより、適切な条件分岐を、プログラマーが構築する必要があります。


switch文とは、選択文の内の1つであり、整数式(制御式)の値に従って、指定するいくつかの文からいずれかを実行するかどうか選択する。 switch文には、制御式と、ブロック(スイッチ本体)の中に、任意の個数のcaseラベルと、高々1つのdefaultラベルが含まれる。 switch文は、制御式の値と等しい整数定数式の値を持つcaseラベルへ制御を移す。 等しい値を持つcaseラベルがなければ、defaultラベルへ制御を移す。 defaultラベルもなければ、ブロックの次の文に制御を移す。

break文はswitchブロックの次の文に制御を移す。 同一のswitch文のブロックの中に、同一の整数定数式の値を持つcaseラベルがあってはならない。

switch文の記述は次のようになっている。

  • switch-case-...-default文
switch (整数式){
	case 整数定数式:
		複文
		break;
	.
	.
	.
	default:
		複文;
}


break文の書き忘れに注意してください。なお、あえてbreak文を書かなくてもよく、その場合をフォールスルーと呼びます。

//例 switch文の使用例
#include<stdio.h>

int main(void)
{
	printf("一桁の数値を入力してください。:");
	int i;
	scanf("%d", &i);
	switch (i){
		case 2:
		case 3:
		case 5:
		case 7:
			printf("入力値は一桁の素数\n");
			break;
		default:
			printf("入力値は一桁の素数ではない\n");
			break;
	}
}

繰り返し文[編集]

while文[編集]

[2] while文とは、繰り返し文の内の1つであり、式(制御式)の論理値が真である間、指定する文(ループ本体)の実行を繰り返す。 while文には、制御式と、ループ本体が含まれる。 while文では、制御式の論理値の評価は、ループ本体の各実行の前に行われる。 while文では、制御式の論理値があらかじめ偽であれば、ループ本体は1度も実行されない。

while文の記述は次のようになっている。

  • while文
while()
	
//例 while文の使用例
#include<stdio.h>

int main(void)
{
	int i=0;
	while(i<5){
		printf("%d ",i);
		++i;
	}
	//「0 1 2 3 4」と表示される。
}

無限ループ[編集]

無限ループとは、文の実行が無限に繰り返されることである。

while(1)
	

上のように記述すると、 while文の制御式の論理値が常に真であるため、 文の実行が無限に繰り返される。 無限ループを用いる場合、 後述するbreak文を用いるなどして、 適切にループから脱出できるようにする必要がある。

// 無限ループ
#include<stdio.h>

int main(void)
{
	while(1){
		printf("ループの中。\n");

		char kotae;
		printf("ループを脱出しますか? 「はい」y /「いいえ」n\n ");
		scanf("%c",&kotae);

		if (kotae=='y')    {
			printf("ループを脱出します。\n");
			break;
		}
	}
printf("ループの外です。\n");
}
※ ウィンドウズのVisual C 系(Visual C++やC#など)のコンパイラだと、上記のコードはエラーになり動作しない。フリーソフト『学習用C言語開発環境』で上記コードが動作することを確認ずみ。


ウィンドウズのVisual C系コンパイラの場合、下記のコードのように、scanfをscanf_sに変えてください。(なお、scanf_sの3つめの引数(下記の例では「10」)の数字は、記憶するデータ数です。)

// Visual C系コンパイラ用
// 無限ループ
#include<stdio.h>

int main(void)
{
	while (1) {
		printf("ループの中。\n");

		char kotae;
		printf("ループを脱出しますか? 「はい」y /「いいえ」n\n ");
		scanf_s("%c", &kotae,10);

		if (kotae == 'y') {
			printf("ループを脱出します。\n");
			break;
		}
	}
	printf("ループの外です。\n");
}


このプログラムを終了するには、yを入力してエンターキーを押せば終了します。入力する「y」は直接入力モード(Windowsの場合は半角英数)にしてください。

なお、文字変数の中身を示す「'y'」のように、文字型の1文字データの中身をあらわすときに使う「' '」については、(二重引用符ではなく)一重引用符であることに気をつけてください。

do-while文[編集]

[3] do-while文とは、繰り返し文の内の1つであり、式(制御式)の論理値が真である間、指定する文(ループ本体)の実行を繰り返す。 do-while文には、制御式と、ループ本体が含まれる。 do-while文では制御式の論理値の評価は、ループ本体の各実行の後に行われる。 do-while文では、制御式の論理値にかかわらず、ループ本体は少なくとも1度は実行される。

do-while文の記述は次のようになっている。

  • do-while文
do
	
while();
//例 do-while文の使用例
#include<stdio.h>

int main(void)
{
	int i=0;
	do{
		printf("%d ",i);
		++i;
	}while(i<5);
	//「0 1 2 3 4」と表示される。
}

for文[編集]

[4] for文とは、繰り返し文の内の1つであり、始めに初期化式を実行し、前実行式の論理値が真である間、「前実行式、文、後実行式」の順に繰り返す。 for文は、「;(セミコロン)」で区切られた、初期化式、前実行式(制御式、継続条件式とも呼ばれる)、後実行式、及びループ本体と呼ばれる文からなる。 初期化式では宣言を行ってもよい。 3つの式はいずれとも省略してもよい。前実行式を省略した場合、0でない定数(真)によって置き換えられる。 for文は、初期化及び後実行を形式的に記述する点でwhile文と異なっている。 一般に、初期化式では変数の初期化を、前実行式では変数に対する継続条件を、後実行では変数の増分または減分を、それぞれ行う。

for文の記述は次のようになっている。

  • for文
for(初期化式; 前実行式; 後実行式)
	
//例 for文の使用例
#include<stdio.h>

int main(void)
{
	int i;
	for(i=0; i<5; ++i)
		printf("%d ",i);	
	//「0 1 2 3 4」と表示される。
}

for文のネスト[編集]

for文の中にfor文を入れることを、ネストまたは入れ子という。

// 例 for文のネストの使用例。九九を表示する。
#include<stdio.h>

int main(void)
{
	int i, j;
	for(i=1; i<=9; ++i){
		for(j=1; j<=9; ++j){
			printf("%2d ", i*j);
		}
		printf("\n");
	}
}

分岐文[編集]

goto文[編集]

[5] goto文とは、分岐文の内の1つであり、指定するラベルへ制御を移す。 goto文は、識別子が指すラベルへ制御を移す。 そのラベルはgoto文を含む関数内になければならない(関数有効範囲)。[6]

goto文の使用は一般には推奨されません。
理由は構造を把握しづらいコード(スパゲティコード)となるからです。
また、goto文は多重にネストされたスイッチやループから抜け出すために使われることがありますが、
それも関数化してreturn文で抜け出すように書き換えることができます。

goto文及びラベルの記述は次のようになっている。

  • goto文
goto 識別子;
  • ラベル
識別子:
//例 goto文の使用例
#include <stdio.h>

int main(void)
{
	int x,y;
	for(y=1;y<=9;++y){
		for(x=1;x<=9;++x){
			if(y*x>=50)goto exit_loop;
		}
	}
	exit_loop:
	printf("九九の表で、最初に50以上になるのは%d×%d。\n", y, x);
}

continue文[編集]

[7] continue文とは、分岐文の内の1つであり、ループ本体の中で使われ、ループ本体の終わりへ制御を移す。 つまり、ただちに制御式が実行され、それからループが繰り返される。 ループ本体の終わりとはcontinue文を含む最も内側のループ本体の最後の文の直後である。

continue文の記述は次のようになっている。

  • continue文
continue;
//例 continue文の使用例
#include <stdio.h>

int main(void)
{
	//1から100までの整数の中で、3の倍数と4の倍数以外を表示する。
	int i;
	for(i=1;i<=100;++i){
		if(i%3==0||i%4==0)continue;
		printf("%d ",i);
	}
}

break文[編集]

[8] break文は、スイッチ本体またはループ本体の中で使われ、 スイッチ本体またはループ本体の直後へ制御を移す。 つまり、スイッチ本体またはループ本体から抜け出すことができる。 また、break文は、break文を含む最も内側のスイッチ本体またはループ本体に対応する。

break文の記述は次のようになっている。

  • break文
break;
//例 break文の使用例
#include <stdio.h>
#include <math.h>

int main(void)
{
	while(1){
		double d;
		printf("正の数を入力してください。(0以下で終了):");
		scanf("%lf", &d);
		if(d<=0)break;
		printf("%fの平方根は%f。\n", d, sqrt(d));
	}
}

return文[編集]

[9] return文とは、分岐文の内の1つであり、実行中の関数を終了し、その呼び出し元へ制御を返す。 式があるかどうかは関数の返却値のデータ型により異なり、 void型の場合は式がなく、それ以外のデータ型の場合は式がある。

return文の記述は次のようになっている。

  • return文
return ;
//例 return文の使用例
#include <stdio.h>

//整数1234を返す関数function
int function()
{
	return 1234;
}

int main(void)
{
	int i=function();//関数functionを呼び、返却値をiに代入する。
	printf("iの値は%d。", i);
}

論理型[編集]

論理型とはデータ型の内の1つであり、真(true)か偽(false)かの2種類の論理値をとる。 JISX3010:2003(ISO/IEC 9899:1999)において、論理型はキーワード_Boolを用いて宣言される。 偽は0,真は1(及び0以外)を用いて表現される。 [10]

ヘッダファイルstdbool.hをインクルードすることで、 _Boolの代わりにbool、0の代わりにfalse, 1の代わりにtrueを用いることができる。 [11] なお、C++用のコンパイラを使用している場合は、stdbool.hをインクルードせずに bool, false, trueを用いることができる。

Very Important!
0が偽、1(及び0以外)が真であることは、このページ全体の前提知識となっており、
とても重要です。

演算子[編集]

比較演算子[編集]

比較演算子についてはC言語/演算子と式#比較演算子を参照せよ。

論理演算子[編集]

論理演算子についてはC言語/演算子と式#論理演算子を参照せよ。

増分及び減分演算子[編集]

増分及び減分演算子についてはC言語/演算子と式#増分及び減分演算子を参照せよ。


脚注[編集]

  1. ^ 『JISX3010:2003』p.101「6.8.4.2 switch文」
  2. ^ 『JISX3010:2003』p.102「6.8.5.1 while文」
  3. ^ 『JISX3010:2003』p.102「6.8.5.2 do文」
  4. ^ 『JISX3010:2003』p.102「6.8.5.3 for文」
  5. ^ 『JISX3010:2003』p.103「6.8.6.1 goto文」
  6. ^ 『JISX3010:2003』p.21「6.2.1 識別子の有効範囲」
  7. ^ 『JISX3010:2003』p.104「6.8.6.2 continue文」
  8. ^ 『JISX3010:2003』p.105「6.8.6.3 break文」
  9. ^ 『JISX3010:2003』p.103「6.8.6.4 return文」
  10. ^ 『JISX3010:2003』p.32「6.3.1.2 論理型」
  11. ^ 『JISX3010:2003』p.182「7.16 論理型及び論理値<stdbool.h>」

参考文献[編集]

  • 日本工業標準調査会『JISX3010 プログラム言語C』2003年12月20日改正