4回目の授業

2回目の授業の課題について



第3章

プログラムの流れの分岐のための制御文



 C言語の命令文のなかの一つとして,プログラムの流れの分岐・繰り返しのための制御文があります。この制御文があるおかげで,一つのプログラムのなかで,

条件によって異なった計算をしたり,

同じような計算を何回もしたり,

ということが自動的にできるわけです。

 前回までの課題で書いていたプログラムでは,この制御文を使用していませんでしたので,電卓を使った計算と比べてもあまり計算の手間は変わらない(プログラムにする意味がない,プログラムするほうがむしろ酷)と思ったひとも多いと思います。

プログラミングをすることの意義(手計算に比べて楽ができること)は,この制御文を使用することで初めて表出してくる,と言えるでしょう。

 今回は,特にプログラムの流れの分岐のための制御文について学びます。


構文その1

 ある条件式が成立している時だけ,ある命令文を実行したい場合には,以下のような形式でプログラムを書きます。
    if( 条件式 ) 条件式が成立した場合に実行すべき命令文 ;

構文その2

 ある条件式が成立しなかった時に実行したい命令文がある場合には,以下のような形式で書きます。
    if( 条件式 ) 条件式が成立した場合に実行すべき命令文 ;
    else 条件式が成立しなかった場合に実行すべき命令文 ;

構文その3

 また,条件式が幾つかある場合には,以下のような形式が使えます。
    if( 条件式その1 ) 条件式その1が成立した場合に実行すべき命令文 ;
    else if( 条件式その2 ) 条件式その1が成立せず, 条件式その2が成立した場合に実行すべき命令文 ;
    else if( 条件式その3 ) 条件式その1, その2が成立せず, 条件式その3が成立した場合に実行すべき命令文 ;
       .
       .
       .
    else 上の全ての条件式が成立しなかった場合に実行すべき命令文 ;

複合文

 上記の構文において,実行すべき命令文が複数あるような場合には,それら複数の命令文を,{ } で囲みます。

例えば構文その2の場合には,以下のようになります。
    if( 条件式 )
    {
        条件式が成立した場合に実行すべき命令文その1 ;
        条件式が成立した場合に実行すべき命令文その2 ;
           .
           .
           .
    }
    else
    {
        条件式が成立しなかった場合に実行すべき命令文その1 ;
        条件式が成立しなかった場合に実行すべき命令文その2 ;
           .
           .
           .
    }
このような,複数の命令文を { } で囲ってひとまとまりにしたものを,複合文(ブロック)と呼びます(Cf. テキスト60ページ)。

(注意) 複合文(ブロック)の後(つまり,} の後)には,セミコロン ; は書いてはいけません。


条件式によく使用される演算子



 上述の構文において,( ) の中に書く条件式としては,数学における,

等式
不等式
論理式

といったものを使用したくなることが自然でしょう。

C言語では,数学における,

等式の役目をする演算子として,等価演算子 ==, !=
不等式の役目をする演算子として,関係演算子 <, >, <=, >=
論理式の役目をする演算子として,論理演算子 &&, ||

がそれぞれ用意されています。


等価演算子(Cf. テキスト50ページTable3-1)

 条件式として,
    a == b
と書くと,ab の値が等しいのかが評価されます。等しい場合には,この条件式は成立し,条件式の評価値としては,真という値を持つことになります。

 また,条件式として,
    a != b
と書くと,ab の値が異なっているかが評価されます。異なっている場合には,この条件式は成立し,条件式の評価値は,真となります。

(注意) 等価演算子は,= ではなくて,== であることに注意して下さい。うっかり= を用いると,代入されてしまいます。すなわち,
    a = b
と書いてしまうと(コンパイルはエラーが出ずにとおりますが),単に ab の値が代入されるだけとなって,期待した動作はしなくなってしまいます。


関係演算子(Cf. テキスト52ページTable3-2)

 条件式として,
    a >= b
と書くと,ab 以上であるかが評価されます。

 関係演算子としてはこれ以外に,
    a <= b
    
    a > b
    
    a < b
があります。これらについては説明は不要でしょう(見た目のとおりです)。


論理演算子(Cf. テキスト63ページTable3-4)

 条件式として,
    条件式その1 && 条件式その2
と書くと,条件式その1 が成立し,かつ 条件式その2 も成立している場合,全体の条件式の評価値が真となります。

 また,条件式として,
    条件式その1 || 条件式その2
と書くと,条件式その1 が成立しているか,または 条件式その2 が成立している場合,全体の条件式の評価値が真となります。つまり,条件式その1条件式その2 のうち,少なくともどちらか一方が成立しているのかを評価するということです。

 このような論理演算子を使えば,例えば,
    a <= b && b < c
と書くことによって,b の値が,a 以上 c 未満であるかどうかを調べることができます。

(注意その1) 上記の,
    a <= b && b < c
のコードのように,演算子を,二つ以上,連続して,使用するコードを記述する場合には,演算子の優先順位の規則があることに注意して下さい。(Cf. テキスト221ページTable7-13)

上記のコードの場合,関係演算子 <=, < と論理演算子 && では,関係演算子 <=, < のほうが優先順位が高いため,先に,
    a <= b
    b < c
が評価されることになるため,期待したとおりの動作をするわけです。

不安なひとは,以下のように,先に実行してもらいたい部分を,明示的に,( )でくくると,より安全です。
(見た目も分かり易くなるという副次的効果もあります。)
    (a <= b) && (b < c)
(注意その2) b の値が,a 以上 c 未満であるかどうかを調べたいときに,初心者がよくやってしまう間違い例として,以下のコードがあります。
    a <= b < c
C言語の原則では,とにかく,左側から順番に演算が実行されていきますから,
上記のコードの場合には,まず最初に,
    a <= b
が実行されて,真または偽の値を持ち,
引き続いて残りの
    (真または偽) < c
が評価されることになります。

コンピュータの中では,真は1という値で表され,偽は0という値で表されることになっていますので,
すなわち,最後は,1または0と,cの値の大きさ比べをすることになってしまいます。

これでは,期待したとおりの動作はしませんよね。気をつけてください。


UNIXがコマンドを探す仕組み



 ユーザーがなんらかのコマンドを入力すると,UNIXシステムは入力されたコマンドと同じ名前のファイルを探します。

このとき,システムが探すディレクトリはあらかじめ決まっています。それは,シェル変数と呼ばれるもののうち,PATHという名前の変数に記憶されています。

現在のPATHの値は,以下のようにして知ることができます。

$ echo $PATH(enter)

例えば,以下のように表示されます。

/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin:

シェル変数PATHに現在納められているディレクトリが,: で区切って,示されています。

UNIXシステムは,ユーザーがなんらかのコマンドを入力すると,上に表示されたディレクトリの順にそのコマンドを探します。

これらのディレクトリを探しても該当するファイルが見つからない場合には,システムは以下のメッセージを表示して,そのコマンドを実行することを諦めます。

コマンド名: command not found

 前前回の授業で,皆さんは実習問題としてList1-3のソースプログラムを入力し,それをコンパイルして,list1-3という名前の実行形式のプログラムを作成しましたね。そのプログラムを実行させるために,

$ list1-3(enter)

と入力したら,

list1-3: command not found

のように表示されてしまったかもしれません。

 また前回,“ホームディレクトリと作業ディレクトリ”のなかで説明したように,UNIXでは特に指定しなければ,作業ディレクトリが作業の対象となります。

そのため,コンパイルしてできあがった実行形式のプログラム(list1-3)は,現在の作業ディレクトリに納められます。

 ところが,システムがコマンドを探すディレクトリ(先程echo $PATHコマンドで表示した)の中には,現在の作業ディレクトリを表す記号である,. が(デフォルト設定では)含まれていません。

そのような場合には,システムは作業ディレクトリの中は探してくれません。したがって,作業ディレクトリに納められているlist1-3を見つけることができなかったというわけです。

システムにlist1-3を特定させるためには,絶対パス名か,相対パス名のどちらかをファイル名の中にちゃんと含めて,入力しなければならなかったということです。

相対パス名で示す場合には,以下のように入力すればよいですね。

$ ./list1-3(enter)

でも,いちいち,./ と入力するのは面倒です。いちいち ./ と入力しなくともよいようにするためにはどうすればよいでしょうか...

シェル変数のPATHに,作業ディレクトリを表す . も追加することができればよさそうですね。

そうするためには,以下のようなコマンドを実行します。

$ PATH=$PATH:.(enter)

上記のコマンドにおいて,$PATH は現在のシェル変数PATHの値を表しています。それに,: で区切りを入れて,新たに . を追加することを,表しています。

PATHの値にちゃんと . が追加されたか,以下のコマンドを入力して確かめてみて下さい。

$ echo $PATH(enter)

このようにシェル変数のPATHの値を設定しておけば,いま自分のいるディレクトリ(作業ディレクトリのことです)にあるファイルについては,./ を追加して入力しなくとも,システムはちゃんと探し出してくれます。

$ list1-3(enter)

ちゃんと実行されました?

いま設定したシェル変数PATHの値は,一度行えばログアウトするまで有効ですので,後はいちいちコマンドの前に ./ を入力する必要はなくなります。

えっ,./ と入力するより PATH=$PATH:. と入力するほうがめんどくさいって?

大丈夫です,ちゃんと楽する方法は,あります。これについては(多分)次回に説明します。


以前に実行したコマンドの再実行(コマンドのヒストリー機能)



 ユーザーがなんらかのコマンドを入力すると,UNIXシステムは入力されたコマンドの履歴(コマンド・ヒストリー)を,覚えていてくれます。この機能により,何回も同じコマンドを実行する際の手間が,大いに省けます。

コマンドをいくつかタイプ・Enterキーを押して実行した後,キーボードの上矢印キーを押してみてください。

すると,シェルプロンプトに,直前に実行したコマンドラインが表示されるはずです。

さらに上矢印キーを押すと,さらに以前に実行したコマンドラインを表示することができます。このようにして,これまでに実行したコマンドを,どんどん,遡ることができます。

容易に推測できると思いますが,下矢印キーを押すと,より最近に実行したコマンドに,戻ることができます。

このようにして表示された,以前に実行済みのコマンドを再実行するには,そこで,Enterキーを押すだけです。

 また,このようにして表示された,以前に実行済みのコマンドを,少し修正してからそれを実行することもできます。

例えば,

$ gcc -o list1-3 list1-3.c(enter)

のように,コンパイルのためのコマンドを実行するつもりが,タイプミスしてしまって,

$ gcc -o list1-3 listtt1-3.c(enter)

と,実行してしまったとします。すると,

clang: error: no such file or directory: 'listtt1-3.c'

のようなエラーメッセージが表示されると思います。

所期のコマンドを実行するためには,もう一度,はじめから,コマンドを全部入力しなおしても,もちろんかまいませんが,
コマンドのヒストリー機能を利用すれば,楽をすることができます。

上矢印キーを押せば,直前に実行したコマンド:

$ gcc -o list1-3 listtt1-3.c

が表示されます。(この時点では,コマンドが表示されるだけで,実行はされない。)

そこで,左矢印キーを何回か押して,余分なtの直前にカーソルをもっていき,そこで,deleteキーを押せば,余分なtを消去することができます。余分なtを2つ消去して,

$ gcc -o list1-3 list1-3.c

と表示されるように,修正してから,Enterキーを押せば,めでたく所期のコマンドを実行することができる,というわけです。

 コマンドラインインターフェースであっても,これくらいの便利さはユーザが享受できるように,作られているのです。


今日の実習問題と宿題



(重要)  宿題の締切は次の授業が始まる前までとします。


自宅での自習環境の構築について(Windows 系 PC の場合)


戻る