「C言語/制御文」の版間の差分

削除された内容 追加された内容
Ef3 (トーク | 投稿記録)
scanf_s("%d", &i, 100); では引数過剰。va_listで引数の数や型を間違えると解析困難なバグの原因になるので注意。 _s 付きのセキュアな関数も、この様なプログラマのミスには無力。
タグ: 2017年版ソースエディター
Ef3 (トーク | 投稿記録)
scanf/scan_f のEOFチェック漏れ多数。
タグ: 2017年版ソースエディター
127 行
printf("数値を入力してください。:\n");
int i;
if (scanf("%d", &i); == EOF) {
printf("End of File に達しました。\n");
return 1;
}
if (i == 0)
printf("入力値は0\n");
141 ⟶ 144行目:
:if文には、if形式、if-else形式の2通りの形式があり、またそれらを組み合わせてもよい。
 
Visual C/C++ では、scanf が「非推奨とマークされている関数」にあたり、設定によってはビルドに失敗します。⇒ [https://docs.microsoft.com/ja-jp/cpp/error-messages/compiler-warnings/compiler-warning-level-3-c4996?view=msvc-160 コンパイラの警告 (レベル 3) C4996]
なお、WindowsのVisual C++系のコンパイラではscanfが使うことが出来ず<ref>MSVCでscanfなどの非推奨とマークされている関数を使いたい場合、#pragma warning(disable : 4996)をソースコードの非推奨とマークされている関数を使う前に書くことで使うことが出来ます。</ref>、上記のコードのままではエラーになるので、かわりにscanf_sという関数を使用します<ref>逆に、BSDのlibcやglibcには scanf_s のような境界チェックインタフェース(C11 Annex K)は用意されていません。</ref>。
 
これを回避するには、ソースコードに <syntaxhighlight lang="C" inline>#pragma warning(disable:4996)</syntaxhighlight> を補って迂回する方法もありますが、
<syntaxhighlight lang="c">
ここでは scanfの境界検査インターフェース版のscanf_sを使った実装を紹介します<ref>逆に、BSDのlibcやglibcには scanf_s のような境界チェックインタフェース(C11 Annex K)は用意されていません。</ref>(scanf_sの3つめの引数 1は、記憶するデータの個数で、第1引数がスカラオブジェクトを指している場合、それは1要素の配列であるとみなされます。<ref name="jtc1-sc22-wg14-n1570-k.3.5.3.2">{{cite book
//例 if文の使用例
| url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
// Visual C 用のコード
| title = N1570 Committee Draft — April 12, 2011 ISO/IEC 9899:201x
| page=592, §K.3.5.3.2 ''The fscanf_s function''
| quote = If the first argument points to a scalar object, it is considered to be an array of one element
| publisher = ISO/IEC}}</ref>なので第3引数の値は 1 です。<ref>過去の版で 10 とされていましたが、正しくは 1 です。</ref>)
 
;境界検査インターフェース版:<syntaxhighlight lang="c">
#include <stdio.h>
 
int main(void) {
printf("数値を入力してください。:");
{
int i;
printf("数値を入力してください。:");
if (scanf_s("%d", &i, 1); == EOF) {
int i;
printf("End of File に達しました。\n");
scanf_s("%d", &i, 1);
return 1;
if (i == 0)
}
printf("入力値は0\n");
else if (i == 10)
printf("入力値は10\n");
else if (i == 1)
printf("入力値は0,1以外\n");
else
printf("入力値は0,1以外\n");
}
 
</syntaxhighlight>
 
184 ⟶ 193行目:
</syntaxhighlight>
 
C99では、<code><stdbool.h></code> が導入され上記のコードは次の様に書くことができます。
<syntaxhighlight lang="C">
#incude <stdbool.h>
 
if (i != fasefalse)
//↓
if (i)
199 ⟶ 208行目:
==== if文のネスト ====
if文の中にif文を入れることを、ネストまたは入れ子という。
;if文のネストの使用例:<syntaxhighlight lang="C" highlight="10,11" line>
#include <stdio.h>
 
205 ⟶ 214行目:
printf("数値を入力してください。:");
int i;
if (scanf("%d", &i); == EOF) {
printf("End of File に達しました。\n");
return 1;
}
if (0 <= i)
if (i <= 10)
211 ⟶ 223行目:
}
</syntaxhighlight>
;if文のネストの使用例(境界チェックインタフェース版):<syntaxhighlight lang="c" highlight="10,11" line>
#include <stdio.h>
 
int main(void) {
{
printf("数値を入力してください。:");
int i;
if (scanf_s("%d", &i); == EOF) {
printf("End of File に達しました。\n");
return 1;
}
if (0 <= i)
if (i <= 10)
225 ⟶ 239行目:
</syntaxhighlight>
 
なお、上の(非Visual C系入れ子コードif例は &&演算子 を用いて、次のように簡潔に書き換えることができます。
 
;ネストしたif文&&演算子を用いで置き換える。:<syntaxhighlight lang="C" highlight=10 line>
#include <stdio.h>
 
233 ⟶ 247行目:
printf("数値を入力してください。:");
int i;
if (scanf("%d", &i); == EOF) {
printf("End of File に達しました。\n");
return 1;
}
if (0 <= i && i <= 10)
printf("入力値は0以上10以下");
296 ⟶ 313行目:
<ref>『JISX3010:2003』p.101「6.8.4.2 switch文」</ref>
 
switch文とbreak 文とを使って、場合分け(複数個の「場合」のある場合)をできます。
 
たとえば
305 ⟶ 322行目:
(以下略)
 
のような場合に、switch文とbreak 文で、場合分けをできます。
 
コードはイメージ的に言うと、
336 ⟶ 353行目:
というコードなら、もしa=2なら、実行されるのは、文m2だけでなく、さらに文m3や文m4も実行してしまいます。
 
このような仕組み(つまりbreak 文がないかぎり、次の関数を実行してしまう)のことを'''フォールスルー'''( ''fall through'' ) と呼びます。
 
{{コラム|フォールスルー|
353 ⟶ 370行目:
defaultラベルもなければ、ブロックの次の文に制御を移します。
 
break 文はswitchブロックの次の文に制御を移します。
同一のswitch文のブロックの中に、同一の整数定数式の値を持つcaseラベルがあってはなりません。
 
377 ⟶ 394行目:
printf("一桁の数値を入力してください。:");
int i;
if (scanf("%d", &i); == EOF) {
printf("End of File に達しました。\n");
return 1;
}
switch (i) {
case 2:
458 ⟶ 478行目:
/* ... */
}
-->
</syntaxhighlight>
-->
</ref>。
 
607 ⟶ 627行目:
</syntaxhighlight>
|}
いずれのコードも、継続条件は常に真で非ゼロの整数(あるいは、非ゼロの整数とみなされる)ので文の実行を無限に繰り返します。
無限ループを用いる場合、後述する break br文を用いるなどして適切にループから脱出できるようにする必要があります。
 
;無限ループを使った例:<syntaxhighlight lang="C">
632 ⟶ 652行目:
</syntaxhighlight>
:※註 過去の版では、scanf の戻り値をチェックしていませんでしたが EOF を返す可能性があり、これを無視すると、それこそ'''無限ループ'''になってしまうので、戻り値も必ずチェックしましょう。また、この部分は return 文による脱出の例にもなっています。
Visual C/C++ では、scanf が「非推奨とマークされている関数」にあたり、設定によってはビルドに失敗します。⇒ [https://docs.microsoft.com/ja-jp/cpp/error-messages/compiler-warnings/compiler-warning-level-3-c4996?view=msvc-160 コンパイラの警告 (レベル 3) C4996]
 
ソースコードに <syntaxhighlight lang="C" inline>#pragma warning(disable:4996)</syntaxhighlight> を補って迂回する方法もありますが、
ここでは scanfの境界検査インターフェース版のscanf_sを使った実装を紹介します(scanf_sの3つめの引数 1は、記憶するデータの個数で、第1引数がスカラオブジェクトを指している場合、それは1要素の配列であるとみなされます。<ref name="jtc1-sc22-wg14-n1570-k.3.5.3.2">{{cite book
| url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
| title = N1570 Committee Draft — April 12, 2011 ISO/IEC 9899:201x
| page=592, §K.3.5.3.2 ''The fscanf_s function''
| quote = If the first argument points to a scalar object, it is considered to be an array of one element
| publisher = ISO/IEC}}</ref>なので第3引数の値は 1 です。<ref>過去の版で 10 とされていましたが、正しくは 1 です。</ref>)
 
;境界検査インターフェース版 無限ループ:<syntaxhighlight lang="c">
680 ⟶ 691行目:
[[#goto文|goto]] identifier ;
[[#continue文|continue]] ;
[[#break 文|break]] ;
[[#return文|return]] expression<sub>opt</sub> ;
 
792 ⟶ 803行目:
このように、continue 文は、そのループ内の以降の処理をスキップしつつも、カウントを1回ぶん、進めます。
 
=== break 文 ===
break 文( break statement)は、最小のswitch文または反復文を囲むものの実行を終了させます<ref name="jtc1-sc22-wg14-n2596-6.8.6.3">{{cite book
| url=http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2596.pdf
| title= N2596 working draft — December 11, 2020 ISO/IEC 9899:202x (E)
| page=127, §6.8.6.3 ''The break statement''
| publisher = [http://www.open-std.org/jtc1/sc22/wg14/www/projects ISO/IEC JTC1/SC22/WG14]}}</ref><ref>『JISX3010:2003』p.105「6.8.6.3 break 文」</ref>。
break 文は、switch本体またはloop本体の中以外に現れてはいけません。
 
break 文の記述は次のようになっている。
;形式:<syntaxhighlight lang="C">
break;
812 ⟶ 823行目:
double d;
printf("正の数を入力してください。(0以下で終了):");
if (scanf("%lf", &d); == EOF) {
printf("End of File に達しました。\n");
return 1;
}
if (d <= 0)
break;
821 ⟶ 835行目:
 
=== for文の中のif文のbreak ===
<!--[[File:Destination Break Programming in If sentence in For sentence.svg|thumb|600px|break 文の移動先の説明図]]-->
<syntaxhighlight lang="C" highlight="5,9" line>
for (・・・) {
836 ⟶ 850行目:
</syntaxhighlight>
kこのように、for文の中にif文があり、if文のブロックの中にbreak;がある場合、一番内側のfor文の終わりに処理が移ります(ここでは、ラベル break_point の位置)。
for文が2つ以上ある場合、break 文で複数のfor文を一度に抜け出すことは'''不可能'''です。
 
=== return文 ===
864 ⟶ 878行目:
</syntaxhighlight>
 
<!-- 型や演算子について「制御文」で触れるのは蛇足では?
== 論理型 ==
論理型とはデータ型の内の1つであり、真(true)か偽(false)かの2種類の論理値をとります
JISX3010:2003(ISO/IEC 9899:1999)において(いわゆる「C99」に相当のJIS)、論理型はキーワード_Boolを用いて宣言されます
偽は0 , 真は1 (及び0以外) を用いて表現される。
<ref>『JISX3010:2003』p.32「6.3.1.2 論理型」</ref>
 
論理型において、ヘッダファイル<stdbool.h>をインクルードすることで、_Boolの代わりにbool、0の代わりにfalse, 1の代わりにtrueを用いることができる<ref>『JISX3010:2003』p.182「7.16 論理型及び論理値<stdbool.h>」</ref>。
 
bool false true いずれも typedef ではなく、マクロで実装すると規定されているので、従前のソースコードと識別子の衝突が起こった場合 #undef することが出来ます。
Bool(ブール)とは、数学の用語です。数学の分野のひとつで『ブール代数』というのがあり、それにあやかって Bool ブールという宣言を使っていると思われます。
 
論理型において、ヘッダファイルstdbool.hをインクルードすることで、
_Boolの代わりにbool、0の代わりにfalse, 1の代わりにtrueを用いることができる。
<ref>『JISX3010:2003』p.182「7.16 論理型及び論理値<stdbool.h>」</ref>
なお、C++用のコンパイラを使用している場合は、stdbool.hをインクルードせずに
bool, false, trueを用いることができる。
 
<pre>
Very Important!
0が偽、1(及び0以外)が真であることは、このページ全体の前提知識となっており、
とても重要です。
</pre>
 
== 演算子 ==
=== 比較演算子 ===
条件判定式の <code>if(a <= b)</code> などの <nowiki> <= </nowiki>や <nowiki> >= </nowiki> や <nowiki> == </nowiki> などが来る部分の演算子のことを比較演算子(ひかく えんざんし)という。詳しくは『[[
{{See also|C言語/演算子と式#比較演算子]]』を参照せよ。}}
 
{|class="wikitable"
904 ⟶ 908行目:
|}
 
これらの論理演算では、条件を満たしているなら、if文などの制御式は 1 を返と評価します。
 
条件が満たされていないなら、 if文などは 0 を返す。
 
条件が満たされていないなら、 if文などの制御式は 0 を返と評価します。
 
たとえば
if (a <= b)
の場合、もし aの値 が b以下 なら、このif文の制御式内部処理的に 1 を返と評価されます。
 
いっぽう、
if (a <= b)
の場合、もし aの値 が bの値 よりも大きいなら、このif文の制御式内部処理的に 0 を返と評価されます。
 
 
古い標準C言語には論理型が無かったので、かつては整数の1と0によってC言語では論理の真偽を表していた。
現在でもコンパイラ内部では数値で真偽を表していると思われる。(C99以降は規格上では、C言語にも論理型が標準でサポートされている。)
 
 
1は「真」、0は「偽」と考えておけば、普通の利用法では問題ない。(なお、C++ には規格の当初から論理型がある。)
 
条件を満たしている場合の1か0かについては、通常の利用法ではコンパイラが判断するので、プログラマーはあまり1か0かといった返される数値については考える必要は無い。
 
プログラマー視点では、もっと単純に考えても平気であり、
:「条件が満たされるなら『真』」、
:「条件が満たされない場合は『偽』」、
と思っていても、実用上は平気な場合が大半である。
 
古いC99より前の標準C言語には論理型が無かったので、かつては整数の1と0と1によってC言語では論理の真偽を表していました。
 
=== 論理演算子 ===
論理演算子については[[{{See also|C言語/演算子と式#論理演算子]]を参照せよ。}}
 
=== 増分及び減分演算子 ===
増分及び減分演算子については[[{{See also|C言語/演算子と式#増分及び減分演算子]]を参照せよ。}}
-->
 
== 脚注 ==
<references/>
 
== 参考文献 ==
* 日本工業標準調査会(当時、現:日本産業標準調査会)『JISX3010 プログラム言語C』2003年12月20日改正
 
[[Category:C言語|せいきよふん]]