• 検索結果がありません。

条件文

ドキュメント内 note.dvi (ページ 48-53)

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 ; }

とすればよい.

ドキュメント内 note.dvi (ページ 48-53)