[前へ]   [目次へ]   [次へ]

条件分岐「if / else」(前編)(C/C++)

(06/06/23 演算子が真を返した時の値の説明を修正)
(06/10/09 偽の読み方が間違っていた点を修正)


今回は、条件分岐のひとつ「 if 」についてです。
if else ともにC/C++の数少ないキーワードのひとつで、条件分岐を処理します。

まず、 if の文法です。

if(条件式)真の時の処理

「条件式」は、真偽値を取ります。
この中の式を評価した結果が「真」ならば、「真の時の処理」を実行するというものです。

   (06/10/09 偽の読み方が間違っていた点を修正)

「真偽値」は、それが成立するかを示す2値で、「真」(しん)と「偽」(ぎ)があります。
(ちなみに、C++の bool 型の値「 true 」は「真」を、「 false 」は「偽」を表します。英語意味そのままです)

   (06/10/09 修正ここまで)

数値を真偽値として評価した場合、「0以外」が真、「0」が偽になります。


さて、「真の時の処理」は、先の条件式の結果が「真」になった時だけ実行される文です。
これは1行構文で、書いた行が終了(行の最後の ; を書く)までで、ソース上で1行に並べても引き伸ばすことはできません。

例を示します。
条件式は n だけで数値を与えています。
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
#include <stdio.h> 

int main(void

   
int n=5;//とりあえず5を入れて定義 
   
if(n)puts("nは0以外-1行構文");//1行構文。nが0以外だったら文字列表示 
   
puts("分岐終了。ここはnがいくつでも通る");
   
return 0; 
}  
実行結果:
nは0以外-1行構文
分岐終了。ここはnがいくつでも通る


次に、5行目で n に5ではなく、0を代入した場合です。
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
#include <stdio.h>

int main(void)
{
   
int n=0;//とりあえず0を入れて定義
   
if(n)puts("nは0以外-1行構文");//1行構文。nが0以外だったら文字列表示
   
puts("分岐終了。ここはnがいくつでも通る");
   
return 0;
実行結果:
分岐終了。ここはnがいくつでも通る
n の値が変化したことにより、6行目の if が偽になり、「真の時の処理」は全てなかったことにされました。

また、「真の時の処理」のところでブロックを開くと、
そのブロックが「真の時の処理」になり、そのブロックの中全てが「真の時の処理」になります。
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
#include <stdio.h>

int main(void)
{
   
int n=5;//とりあえず5を入れて定義
   
if(n){//この場所でブロックを開く。条件は初回と同じ
      
puts("nは0以外-ブロック構文");
      puts(
"ここもifの範囲内。真の時のみ実行される");
   }
//ifブロックの終点
   
puts("分岐終了。ここはnがいくつでも通る");
   
return 0;
実行結果:
nは0以外-ブロック構文
ここもifの範囲内。真の時のみ実行される
分岐終了。ここはnがいくつでも通る


次に、5行目で n に5ではなく、0を代入した場合です。
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
#include <stdio.h>

int main(void)
{
   
int n=0;//とりあえず0を入れて定義
   
if(n){//この場所でブロックを開く。条件は初回と同じ
      
puts("nは0以外-ブロック構文");
      puts(
"ここもifの範囲内。真の時のみ実行される");
   }
//ifブロックの終点
   
puts("分岐終了。ここはnがいくつでも通る");
   
return 0;
実行結果:
分岐終了。ここはnがいくつでも通る
この場合は、6行目で開いたブロックの終点、9行目までが if の範囲になり、
if が偽になったことにより範囲内は全てなかったことにされました。


さて、これだと「0以外」しか判定できないので他の条件式を使うために「条件(論理)演算子」があります。
条件演算子には
!(否定),==(等価),!=(以外),<(より小さい),<=(以下),>(より大きい),>=(以上),||(いずれか),&&(かつ)
の9種類があり、不等号、等号系は左辺の右辺に対する()内の意味として解釈し、結果を真偽で返します。
例えば、
5<=10
だった場合、「5は10以下」という意味になります。
これは正しいのでこの演算の結果は真になります。
逆に、
15<=10
だった場合、「15は10以下」という意味になります。
これは間違っているのでこの演算の結果は偽になります。

また、 ! (否定)は真偽を逆転、 || (いずれか)は左右どちらかが真の時真を返し、 && (かつ)は左右両方が真の時真を返します。
! (否定)は真偽を逆転するので
!(a==b)
a!=b

の二つの条件式の結果は同じになります。(上は「aはbと等しい」を反転、下は「aはb以外」)
「aはbと等しい」を満たす時、「aはb以外」は絶対に満たしません。
そして、「aはbと等しい」の結果を否定するのでこれが真の時、偽になります。
結果として両方とも偽になります。(逆もまた同様です)

また、
0||1
は「0と1のいずれかが真」という意味になります。
数値を真偽値として評価した場合の真は「0以外」なので0は偽、1は真です。
条件は「いずれかが真」なので、これは正しく、この演算の結果は真になります。

一方、
0&&1
は「0と1の両方が真」という意味になります。
条件は「両方が真」なので、これは間違っており、この演算の結果は偽になります。

これらを組み合わせると複雑な条件を作れますが、
やりすぎるととても読みづらくなります。
例えば、
(a&&b)&&(((a>10)&&(b<-10))||(a>-10)&&(b<-100))
これは「aとbは真でかつ、「aが10より大きくbが-10より小さい」または、「aが-10より大きくbが-100より小さい」」という意味です。
どうでしょう?読みづらいでしょう?(笑)
これはあまり脈絡がないのでとても読みづらい訳で、
((a>='0')&&(a<='9'))||((a>='a')&&(a<='z'))||((a>='A')&&(a<='Z'))
のような意味が分かりやすいものは長くても比較的読みやすいです。
(これは a がアルファベットか数字の時に真になります。
   '0'という表記は文字 0 の文字コードを指します。
   ASCIIコード表 により、文字 0 以上文字 9 以下を両方満たすのは、文字 0〜9、つまり数字になります)

もちろん、定数同士で比較しても特に意味はありません。
通常は少なくともどちらか一方を変数など、値が変化するものを使います。

   (06/06/23 演算子が真を返した時の値の説明を修正)

なお、真(0以外)の場合に条件(論理)演算子が返す値は1です。
しかし、真偽を返すとされる関数には0でない1以外の値を真として返す関数もあるため、混乱の元にもなります。
というわけで、条件演算子が返した値も条件式にだけ使った方がいいでしょう。
(条件演算子が返すのはint型の整数なので、変数に代入しちゃったりできます・・・あんまり使わないけど(笑))

   (06/06/23 修正ここまで)

各演算子の簡単な説明は「 演算子 」の項を参照してください。

条件演算子はこれまでの演算子同様に使用します。

例としては以下のようになります。
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
#include <stdio.h>

int main(void)
{
   
int n=5;//とりあえず5を入れて定義
   
if(n==5){//「nが5ならば」
      
puts("nは5です");
   }
   puts(
"分岐終了");//ここはnがいくつでも通る
   
return 0;
実行結果:
nは5です
分岐終了


6行目の条件式に注目です。

A B  C
n == 5


通し記号種別
Aint5変数(n)
B演算子(==)(2項、論理、優先9、結合→)
Cconst int5定数

== (等価)演算子は、両辺の値が等しければ真を、異なれば偽を返す演算子です。
型は大半の算術演算子同様、一部の装飾を除き一致する必要があります。
また、異なる型だった場合は変換を試みます。

この例では、両辺(A、C)はともに5なので、真となる値を返します。
というわけで、評価終了後は

A
vtemp1


通し記号種別
Aint一時変数
となり、条件式が成立します。
もしここで、両辺が一致しなければ偽が返され、条件式は不成立になります。


また、条件式と普通の式は実質同じ評価が行われます。

これは結構重要なことです。
特にBASIC系の言語をやったことがある人は、
= (代入)演算子が条件式においても代入を行うということに気をつける必要があります。

例としては、以下のようなパターンです。
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
#include <stdio.h>

int main(void)
{
   
int n=5;//とりあえず5を入れて定義
   
if(n=5){//「nが5ならば」と書いたつもり
      
puts("nは5です");
   }
   puts(
"分岐終了");//ここはnがいくつでも通る
   
return 0;
実行結果:
nは5です
分岐終了


一見、さっきとそっくりで正しく動作しているように見えますが、立派にバグがあります。
この例では、nがいくつであっても上の結果になってしまいます。

例えばnに1を代入した場合として、6行目の条件式をよく見ると・・・

A B C
n = 5


通し記号種別
Aint1変数(n)
B演算子(=)(2項、算術、優先2、結合←)
Cconst int5定数
さっきとは異なり、演算子Bは代入演算子です。

Bは「代入」演算子なので、nに5を代入します。
この評価が終わったあとは・・・

A
n


通し記号種別
Aint&5一時変数(参照先:n)

となり、これは5、つまり真となり条件式は成立します。

このように、この二つはよく似ているので、
うっかり等価演算子のつもりで代入演算子を書いてしまい、
分岐が全く効かないという事態にしばしばなったりするかもしれません。

しかも一見等価演算子にも見えるため、気付きにくいので厄介です。
特になれないうちは = (代入)と == (等価)をしっかり確かめるようにしてください。

また、これらの条件演算子は配列を比較することはできません。
配列を比較しようとしてもポインタ値に変換されてしまい、各要素の比較にはなりません。


さて、長くなったので2分割、後編は「それ以外の場合」を意味する「 else 」です。

[前へ]   [目次へ]   [次へ]

プログラミング講座 総合目次

最終更新 2008/10/17