6.9 いくつかのプログラム
6.10.8 条件文
条件文とは,条件によって実行を分岐させるための制御文である.
6.10.8.1 if文
最も簡単な条件文は if文である. if文は以下のような構文を持つ.
if (式) 文
ここで,式は省くことはできない. ここで,式は算術型もしくはポインタでなくてはならない.
このようなif文は,次のように制御される.
• 式が0 でなければ,文が実行される.
6.10.8.2 if–else 文
if–else文は以下のような構文を持つ.
if (式) 文1 else
文2
ここで,式は省くことはできない. ここで,式は算術型もしくはポインタでなくてはならない.
このようなif–else 文は, 次のように制御される.
• 式が0 でなければ,文1が実行される.
• 式が0 であれば,文2が実行される.
if–elseは繰り返して使うことができ,次のような形になる.
if (式1) 文1
else if (式2) 文2
else if (式3) 文3
else 文4
この形では, 式1から順番に式が評価される. 式1が 0であれば,式2を評価するという順番で, 条件式 が評価されていく.
Example 6.10.19 次の例はnが 0か正か負かを判定している.
int n;
if (n == 0)
printf("n is equal 0\n") ; else if (n < 0)
printf("n is negative\n") ; else
printf("n is positive\n") ;
Remark 6.10.5 上のExampleの最初のif (n == 0)はif (!n)と書くこともできるが,後のif--else との整合性を考えると,ここはn == 0と書いた方が良い.
C の標準関数の中で,int型の変数が 英小文字であるかどうかを判定するための関数として, islower 関数がある. cが英小文字であるときにはislower(c) は 1 となり,そうでないときには0 となる. この ような関数を利用するときには,
if (islower(c))
と書けば(そのままこの文章を英語で読めば)非常にわかりやすい.
しかし, Cの標準関数の中で,2つの文字型の配列(文字列)が同一であるかどうかを判定する関数とし て,strcmpがある. char *s, *tに対して,strcmp(s,t)はその辞書式順序に従う差を与える. すなわち, s,tの指し示す文字列が同一内容であるときに0を返すため,
if (strcmp(s,t) == 0)
によって,s, tが等しいときの分岐を表すことになる.
条件式を簡潔に書くことは,プログラムを見易くするために重要なことであるが,条件式を余りに簡潔に しすぎると思わぬバグを産む可能性がある.
if–else文には曖昧な構文がある.
Example 6.10.20 次の例はelseがどちらのifに結び付いているかわかりにくくなった例である.
if (n > 0) if (a > b)
z = a ; else
z = b ;
ここで,文法的にはelse は2番めのifに結び付いている.
このような表現を避けるため,if–elseで制御される文は{}をつけて複文にすることが望ましい.
if (n > 0) {
if (a > b) z = a ; }
else z = b ;
if (n > 0) {
if (a > b) z = a ; else z = b ; }
6.10.8.3 switch文
switch文は以下のような構文を持つ.
switch (式) { case 定数式: 文 case 定数式: 文 default: 文 }
ここで, 式は省くことはできない. 式は整数型でなくてはならない. default はなくても良い. 一つ の switchに許されるdefaultは高々一つ. case の定数式で重複するものがあってはならない. 一つの switch文には少なくとも257個のcaseラベルを用いることが出来る. また,switchは入れ子にできる.
このようなswitch文は,次のように制御される.
• 式の結果に応じて,定数式で表されたどれかの式に制御が移る.
• もし,defalutが存在し,式の結果が定数式のどれとも一致しない時には, defaultが実行されるが,
defaultが存在しない時には,どれも実行されない.
ここで,式は,副作用も含めて,全て最初に評価され, 整数への格上げを受けた定数式と比較される.
switchで重要なことは,次のことである.
• マッチした定数式に対応する文が実行された後, 制御は次のcaseラベルもしくは default ラベル を持つ文に移され,無条件に実行される.
Example 6.10.21 この例では,nの値によって,結果を印字しようとしているが,期待通りには動かない.
int n ; switch (n) {
case 1: printf("n は 1 だよ\n") ; case 0: printf("n は 0 だよ\n") ; case -1: printf("n は -1 だよ\n") ;
default: printf("n は 1, 0, -1 のどれでもない\n") ; }
これは,n = 1の時, n は 1 だよ
n は 0 だよ n は -1 だよ
n は 1, 0, -1 のどれでもない という結果をうち出す.
このようにswitch文の中で, 一つのcaseラベルを実行し,次のcaseラベルに制御が移ってしまうこと
を“Fall Throught” と呼ぶが, Fall Throughtを使う仕様はバグの原因となる.
このようなことを避けるためには,break 文を使う.
Example 6.10.22 上のExampleを改良した.
int n ; switch (n) {
default: printf("n は 1, 0, -1 のどれでもない\n") ; break ;
case 1: printf("n は 1 だよ\n") ; break ;
case 0: printf("n は 0 だよ\n") ; break ;
case -1: printf("n は -1 だよ\n") ; break ;
}
この改良により, この switch 文ではどれか一つのラベルに対する文しか実行しなくなる. 出来れば
defaultラベルを持つ文は一番最後につけよう. その方がわかりやすい.
6.10.8.4 if文と浮動小数点演算
if文の場合にも, 繰り返し文と同様に,浮動小数点演算の誤差に注意しなければならない.
Example 6.10.23 1.0 から0.1 を0.0になるまで繰り返し減算する.
#include <stdio.h>
int main(int argc, char **argv) {
double x=1.0 ; while(1) {
x -= 0.1 ;
printf("x = %f\n", x) ; if (x == 0.0) break ; }
return 0 ; }
Solaris 2.6上の gcc 2.95.1では正常に動作しない. これは, 次のように書換えてみると現象を良く理解
できる.
#include <stdio.h>
int main(int argc, char **argv) {
double x=1.0 ; while(1) {
x -= 0.1 ;
printf("x = %.16f\n", x) ; if (x == 0.0) break ; }
return 0 ; }
この場合, 0.0になっていると思われる時の前後の出力は, x = 0.1000000000000001
x = 0.0000000000000001 x = -0.0999999999999999
となり,正しく0.0にはなっていない.
この例を正しく動作するように直すには,
#include <stdio.h>
#include <math.h>
#define EP 1.0E-12
int main(int argc, char **argv) {
double x=1.0 ; while(1) {
x -= 0.1 ;
printf("x = %.16f\n", x) ; if (fabs(x) < EP) break ; }
return 0 ; }
とすればよい.