「C言語/制御文」の版間の差分
削除された内容 追加された内容
→分岐文: cleanup タグ: 2017年版ソースエディター |
→条件分岐: cleanup タグ: 2017年版ソースエディター |
||
1 行
== 条件分岐 ==
「もし明日が晴れだったら、出かけよう。晴れでなかったら、出かけないでいよう。」のように、何かの条件によって処理が分かれることを条件分岐といいます。
C言語では条件分岐の為に、選択文( ''Selection statements'' )が用意されています。
=== if文 ===
==== 具体例 ====
if文( ''if statement'' )は、もっと単純な選択文です。
たとえば、プログラムでは「もし変数aが4だったら、変数gには15を代入しなさい」は、
<syntaxhighlight lang="C">
if (a == 4)
</syntaxhighlight>
<syntaxhighlight lang="C">
if (a == 4)
</syntaxhighlight>
と書かれた場合、もしaが <var>4</var> なら条件
if 文は条件式が 0 でなけでば、続く文を実行し、0 であれば実行しません。
;if文の例:<syntaxhighlight lang="C">
#include <stdio.h>
int main(void) {
int a = 4;
printf("aは%dです。\n", a);
int g = 15;
printf("gは%dです。\n", g);
}
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang="text">
aは4です。
gは15です。
</syntaxhighlight>
このように、if文の条件式(上記のプログラムの場合は <code>a==4</code>)が満たされるときには中カッコ{}の内容が実行されます。
なお、条件式を書くときの等号は、<code>==</code>になります。条件式における等号は、<code>=</code>ではありません(C言語では、ひとつだけのイコール記号<code>=</code>は演算子です。)。
もし、条件式を変更すると、・・・
<syntaxhighlight lang="C">
#include <stdio.h>
int main(void) {
int a = 6;
printf("aは%dです。\n", a);
if (a ==
int g = 15;
printf("gは%dです。\n", g);
}
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang="text">
aは6です。
</syntaxhighlight>
if文の外の文は実行されますが、if文の内側は実行されません。
==== よくある間違い ====
* ==演算子と=演算子の誤用
if文でよくある間違いの1つに、
==演算子の代わりに=演算子を誤って用いることがあ
=演算子は左辺に右辺を代入した後
コンパイルエラーとはなりませんが、予期しない結果を引き起こします<ref>clang や gcc であれば、コマンドラインオプション -Wparentheses で警告されるようになります。</ref>。
<syntaxhighlight lang="C">
if (i = 0)
printf("iは0");
</syntaxhighlight>
上の例では、式「i=0」の評価結果はiの値によらず偽となってprintf関数の文は常に実行されません。
* 誤った;の付加
if文でよくある間違いの1つに、if文の後に誤った;を付加することがある。
<syntaxhighlight lang="C">
if (i == 0);
printf("iは0");
</syntaxhighlight>
上の例では、if文の式が真の時に実行される文がなく、printf関数の文は常に実行されます<ref>clang では、コマンドラインオプション -Wempty-body で警告されるようになります。</ref>。
==== if文の書式 ====
if文には次の2通りの形式がある。
;if形式:<syntaxhighlight lang="C">
if ( 式 )
文
</syntaxhighlight>
;if-else形式:<syntaxhighlight lang="C">
if (式)
文1
else
文2
</syntaxhighlight>
どちらの形式でも、文1は、式の比較が0に等しくない場合に実行されます。
if-else形式では、式の比較値が0に等しい場合、文2が実行される。
; 制約事項
: if文の制御式は、スカラー型でなければなりません。
; スカラ型
: 算術演算型とポインタ型の総称
: アグリゲート型
: 配列型と構造体型の総称
* 懸垂if
if文もまた文であるため、上の2つの形式を組み合わせて次のように連続して記述することもできます。
<syntaxhighlight lang="C">
if (式)
文
else if (式)
文
else if (式)
文
.
.
else
文
</syntaxhighlight>
elseは、構文で許可されている場合、字句的に最も近い先行詞と関連付けられます。
if文に限りませんが、文はブロック(複合文; ''Compound statement'' )であってもかまいません。
;if文の使用例:<syntaxhighlight lang="C">
#include <stdio.h>
int main(void) {
printf("数値を入力してください。:\n");
int i;
scanf("%d", &i);
if (i == 0)
printf("入力値は0\n");
else if (i == 1)
else
printf("入力値は0,1以外\n");
}
</syntaxhighlight>
;まとめ
:if文とは、選択文の内の1つであり、式(制御式)の論理値に従って、指定する文(副文)を実行するかどうか選択します。
:if文には、if形式、if-else形式の2通りの形式があり、またそれらを組み合わせてもよい。
なお、WindowsのVisual C++系のコンパイラではscanfが使
<syntaxhighlight lang="c">
195 ⟶ 170行目:
: [ツール] -> [オプション] -> [デバッグ] -> [デバッグの停止時に自動的にコンソールを閉じる]
を無効にします。
}}
==== if文の書き換え ====
if文は、0が偽、0以外が真であることを用いて、次のように簡潔に書き換えることができ
<syntaxhighlight lang="C">
if (i != 0)
213 ⟶ 184行目:
</syntaxhighlight>
C99では、stdbool.h が導入され上記のコードは次の様に書くことができ
<syntaxhighlight lang="C">
#incude <stdbool.h>
228 ⟶ 199行目:
==== if文のネスト ====
if文の中にif文を入れることを、ネストまたは入れ子という。
;if文のネストの使用例:<syntaxhighlight lang="C">
#include <stdio.h>
int main(void) {
printf("数値を入力してください。:");
int i;
scanf("%d", &i);
printf("入力値は0以上10以下");
}
</syntaxhighlight>
;if文のネストの使用例(境界チェックインタフェース版):<syntaxhighlight lang="c">
#include <stdio.h>
261 ⟶ 225行目:
</syntaxhighlight>
なお、上の(非Visual C系のコードの)例は &&演算子 を用いて、次のように簡潔に書き換えることができ
;if文で&&演算子を用いる。:<syntaxhighlight lang="C">
#include <stdio.h>
int main(void) {
printf("数値を入力してください。:");
int i;
scanf("%d", &i);
if (0 <= i && i <= 10)
printf("入力値は0以上10以下");
}
</syntaxhighlight>
279 ⟶ 241行目:
==== if文での文字列の比較 ====
C言語のif文で、ある文字列変数が、指定する文字列に等しいかどうかを判定するには、<code>==</code> ではなく、<code>strcmp</code>を使う。
strcmpを使うために <code>#include <string.h></code> で string.h をインクルードします。
<code>strcmp</code> 関数は文字列どうしを比較して、一致したときに(1ではなく)0を返す標準ライブラリ関数です。
;コード例:<syntaxhighlight lang="C" highlight="7,11" line>
#include <stdio.h>
#include <string.h>
int main(void) {
char a[30] = "taro";
if (strcmp(a, "taro") == 0) {
printf("太郎だった \n");
}
if (strcmp(a, "taro") != 0) {
printf("太郎でない \n");
}
}
</syntaxhighlight>
;実行結果
太郎だった
下記のようなにも書ける。
;コード例:<syntaxhighlight lang="C" highlight="7,11" line>
#include <stdio.h>
#include <string.h>
int main(void) {
char a[30] = "taro";
if (!strcmp(a, "taro")) {
printf("太郎だった \n");
}
if (!!strcmp(a, "taro")) {
printf("太郎でない \n");
}
}
</syntaxhighlight>
;実行結果
太郎だった
{{コラム|C言語では文字列は第一級オブジェクトではない|
C言語では、Fortran のように配列全体のコピー(=配列の代入)や四則演算はできません。
このことを以って、配列はC言語の[[W:第一級オブジェクト|第一級オブジェクト]]ではないと言われます。
文字列も文字の配列(に番兵として '\0' で終端したもの)なので、配列と同様に文字列も第一級オブジェクトではありません。
文字列の代入や比較、結合には <string.h> で定義された、標準ライブラリ関数<ref>過去の編集で、標準ライブラリ関数を誤って組み込み関数と表記していましたが、C言語に組み込み関数はありません。</ref>を使う必要があります。
}}
=== switch文 ===
369 ⟶ 310行目:
(イメージ)
switch(a) {
case 1: 文m1 ; break ;
case 2: 文m2 ; break ;
378 ⟶ 319行目:
のような記法になります。
まず、実行したい
switch文に遭遇したときに、どのcaseに移動するかを決めます。
C言語の仕組みとして、もしbreakがないとそのまま次の文を実行します。
389 ⟶ 327行目:
つまり、
switch {
:case 1: 文m1 ;
:case 2: 文m2 ;
398 ⟶ 336行目:
というコードなら、もしa=2なら、実行されるのは、文m2だけでなく、さらに文m3や文m4も実行してしまいます。
このような仕組み(つまりbreak文がないかぎり、次の関数を実行してしまう)のことを'''フォールスルー'''( ''fall through'' ) と呼びます。
{{コラム|フォールスルー|
ほかのプログラム言語では、switch文においてフォールスルーが廃止されている場合もあります。
たとえばグーグルの開発した「Go言語」というプログラム言語では、原則的にフォールスルーは行わないようになっています。
例外的に現在実行中のcaseの下にある次のcaseを実行したい場合にだけ「fallthrough」というキーワードを付け加えるという仕組みをとっています(もちろんC言語には「fallthrough」キーワードはありません<ref name="fallthrough">C++には fallthrough というアトリビュートが有り、ISO/IEC/JTC1/SC22/WG/14 に [http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2408.pdf N2408 The fallthrough attribute]として提案され、C23に追加される予定です。</ref>)。
[[Ruby]]ではこの混乱を避けるためswitch/case文ではなく、case/when(then)文を用意し転換教育がスムースに行われるよう配慮されています。
}}
switch文とは、選択文の内の1つであり、整数式(制御式)の値に従って、指定するいくつかの文からいずれかを実行するかどうか選択します。
switch文には、制御式と、ブロック(スイッチ本体)の中に、任意の個数のcaseラベルと、高々1つのdefaultラベルが含まれます。
switch文は、制御式の値と等しい整数定数式の値を持つcaseラベルへ制御を移します。
等しい値を持つcaseラベルがなければ、defaultラベルへ制御を移します。
defaultラベルもなければ、ブロックの次の文に制御を移します。
break文はswitchブロックの次の文に制御を移します。
同一のswitch文のブロックの中に、同一の整数定数式の値を持つcaseラベルがあってはなりません。
switch文の記述は次のようになっている。
*switch-case-...-default文
;形式:<syntaxhighlight lang="C">
switch (整数式) {
case 整数定数式:
文
443 ⟶ 371行目:
</syntaxhighlight>
;switch文の使用例:<syntaxhighlight lang="C">
#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;
}
}
</syntaxhighlight>
==== 発展的な話題 ====
for文などとswtich文とを組み合わせて、配列の順位ごとの初期化する例。番号ごとに代入できます。
;[https://paiza.io/projects/p6-CS51SWQ9La0qaZlI0ig?language=c for文との組み合わせ使用例]:<syntaxhighlight lang="C">
#include <stdio.h>
int main(void) {
// 商品リスト
struct product_list {
char name[32]; // 商品名
int price; // 価格
};
struct product_list drinks[10]; // 構造体配列の宣言
for (int i = 0; i <= 2; i = i + 1) {
switch (i) {
case 1: drinks[i] = (struct product_list){ "オレンジジュース", 240 }; break;
case 2: drinks[i] = (struct product_list){ "野菜ジュース", 190 }; break;
default :
}
}
}
}
</syntaxhighlight>
;実行例:<pre>
商品『牛乳』の値段は120 円
商品『オレンジジュース』の値段は240 円
525 ⟶ 427行目:
</pre>
上記のコードでは、構造体(こうぞうたい; ''structure'')
{{See also|C言語/構造体・共用体}}
上記コードでは、forで0,1,2を巡回しているので、すべてのcaseが実行される事にな
このようにわざわざswitch文を使って代入する事のメリットは、配列の順番を入れ替えたい場合、たとえば「野菜ジュース」と「オレンジジュース」の順番を入れ替えたい場合に、コピーペーストでコードをそのまま移動すれば終わるという長所がある。
いっぽう、もし配列に数字を直接入れてしまうと、たとえば
:<syntaxhighlight lang="C">
drinks[2] = (struct product_list){ "野菜ジュース", 190 };
</syntaxhighlight>
のように、配列番号2番などとコードを入れ替えてしまうと、順番を入れ替える際に、番号を書き直す手間が発生してしまう。
上記コード例では、個々の番号のデータを名称と価格の2種類だけのデータなので、まだ入れ替えの作業数は少ないが、もし、もっとデータ種類の多いデータを扱っている場合だと、直接代入の方式のコードだと、入れ替えの負担が膨大になってしまい、事実上は入れ替えが不可能になりかねない。
なので、switch文などを使って、入れ替えなどの編集しやすくする事で、長所がある
:<syntaxhighlight lang="C">
case 2:
nomimono[i].kakaku = 190;
strcpy(nomimono[i].syouhinmei, "野菜ジュース");
break;
</syntaxhighlight>の様に構造体配列の要素の要素を1つ1つ埋めていたので保守性に問題が有りました。
<!--
:<syntaxhighlight lang="C">
struct product_list drinks[] = {
{ "牛乳", 120 },
{ "オレンジジュース", 240 };
{ "野菜ジュース", 190 },
};
for (int i = 0; i < sizeof drinks / sizeof drinks[0]; i++) {
/* ... */
}
-->
</syntaxhighlight>
</ref>。
== 繰り返し文 ==
|