真偽値と選択文

こんにちは、めのんです!

この連載が対象としているのはプログラミング初心者ではありませんし、比較的文法が似通ったPHPのプログラミング経験を前提としているのでこれまで詳しく解説してこなかった言語仕様がいくつかあります。
今回はそんな中から真偽値と選択文を解説することにします。

真偽値

真偽値というのは真か偽か、trueからfalseかを表す値のことです。
PHPにも同じ概念がありますね。

PHPの場合、いろんな型の値が真偽値に変換されますが、そのルールを把握するのは結構大変だと思います。
たとえばこんな感じです。

<?php
function f($arg)
{
  if ($arg)
    echo 'true' . PHP_EOL;
  else
    echo 'false' . PHP_EOL;
}

class A
{
};

f(1);       // true
f(true);    // true
f(null);    // false
f("0");     // false
f("1");     // true
f("a");     // true
f("x");     // true
f(new A()); // treu
f([]);      // false
f([ 1 ]);   // true
f([ 0 ]);   // true

これに比べてCは簡単です。
数値やポインタがゼロかゼロでないかだけで真偽が決まります。
ゼロであれば偽、ゼロでなければ真になります。

#include <stdio.h>
#include <stdbool.h>

void f(_Bool f)
{
  if (f)
    puts("true");
  else
    puts("false");
}

struct A
{
};

int main(void)
{
  f(1);    // true
  f(0);    // false
  f(NULL); // false
  f("0");  // true
  f("1");  // true
  f("a");  // true
  f("x");  // true
  struct A a;
  f(a);    // コンパイルエラー
  int b[1] = { 0 };
  f(b);    // true
  int c[1] = { 1 };
  f(c);    // true
  return 0;
}

先ほどのPHPのサンプルコードとできるだけ近い形にしましたが、ちょっと違うところもあるので気を付けてください。

数値の場合は文字通りゼロかどうかで真偽が決まります。
空ポインタ(NULL)の場合はゼロに相当しますので偽になります。

文字列はポインタに暗黙的に変換された上でさらに真偽値に変換されます。
文字列リテラルのアドレスは決してNULLではありませんから真になります。
配列の場合も同様です。

構造体型の場合は真偽値に変換できませんのでコンパイルエラーになります。
上のサンプルコードの26行目がそうです。

_Bool型

Cには真偽値の型として「_Bool」型があります。
_Bool型は0または1の値だけしかとりません。

「_Bool」というのはおかしな名前ですよね。
こんな名前になったのは大人の事情があるようです。
「stdbool.h」ヘッダをインクルードすれば「bool」という名前が使えます。
ほかにも「stdbool.h」ヘッダでは「true」「false」という名前の定数(正確にはマクロ)が定義されています。

ところで、_Bool型はC99から導入されたので、伝統的には今でも真偽値にint型が使われることが多いようです。
int型だと0と1だけではなくいろんな値をとりますから、1やtrueと比較すると思わぬ結果になります。
!= 0や!= falseなら問題ありません。

if (x == 1)     // 期待通りに動かないかも
if (x == true)  // 〃

if (x != 0)     // これならOK
if (x != false) // 〃
if (x)          // これもOK

選択文

これまでにもちゃんと説明しないまま選択文が登場することが何度もありました。
ついさっきもif文を使ってしまいましたね。

Cの選択文には「if」文と「switch」文があります。
これから順に解説していきますね。

if文

Cのif文は基本的にはPHPと同じです。
elseが使える点も同じです。
ifのあとの丸括弧の中には真偽値として評価可能な式であれば何でも書くことができます。

PHPと違う点を次に挙げます。

  • 制御構造に関する別の構文は使えません
  • elseifは使えませんのでelse ifを使ってください

if文でブロックを使用することもできますので、その中だけで有効な宣言を行うこともできます。
たとえばこんな感じです。

if (x)
{
  int a = 123;  // ブロックの中で宣言
  ...
}

switch文

Cにもswitch文があります。
使い方もPHPのswitch文とほぼ同じですが、次の点が異なります。

  • 制御構造に関する別の構文は使えません
  • caseラベルに書けるのは整数値だけです

caseラベルに書けるのが整数値だけというのが違和感があるかもしれませんね。
PHPでは文字列でも浮動小数点数でも書けますから。
これについてそういう仕様だと思って覚えていただくしかありません。

switch文でもブロックを使いますので、その中で宣言を行うことができます。

switch (x)
{
  int a;
case 1:
  int b;
  ...
}

上のようにswitch文の中でも宣言はできますし、初期化子も与えることができるのですが、宣言した場所を実行パスが通過しないと初期化されなくなってしまいます。
混乱を避けるために初期化子は書かない方がいいと思います。


今回の解説は以上となります。
次回は繰返し文を解説しようと思います。
どうぞご期待ください!