W.I.S. Laboratory
menu-bar

C言語 _Bool


C言語の_Bool型はC++のbool型と同等なのか

1972年に登場したC言語には、真偽値を格納するための「Boolean型」が27年間存在しなかった。
なのでほとんどのプログラマーは、int型の変数に「0 = false / 0以外 = true」として真偽値を格納していた。
intや数字だと分かりにくいので、大抵は下のようにtypedefやdefineして読みやすくしていたものの、中身はintなので当初は2バイト、32bitの時代になると4バイト消費していたわけだ。

typedef int bool;
#define true 1
#define false 0

上の例では小文字で書いているが、実際は「BOOL」「TRUE」などと全大文字で書いていたプログラマーが多い。(標準Cでそういう決まりがあるわけではないが)
実際、WindowsのAPI周りもintをtypedefした「WINBOOL」、さらにそれをdefineした「BOOL」、1と0をdefineした「TRUE」「FALSE」があちこちに見受けられる。
C言語を拡張して1983年に登場したC++にはbool型があり、これをsizeofで調べると1バイトと表示される。
C言語を拡張することなくオブジェクト指向型言語へシフトして1984年に出てきたObjective-Cでは、下のようにsigned char型をtypedefした「BOOL」が使用されている。

typedef signed char BOOL;
#define YES (BOOL)1
#define NO (BOOL)0

なのでObjective-Cにおいても真偽値を格納する変数はC++と同じく1バイトだ。
余談だがObjective-Cでは「YES / NO」で真偽値を記述する。(現在は_Bool型も使える)
C言語もsigned charに格納すれば1バイトで済む(signed charの0も真偽判定をすればfalseになる)のだが、intに格納することがすっかり慣例化してしまい、長らく真偽値格納のために4バイトも食うのはC言語くらいのものだった。(charは文字型であり数値を収める型ではないという設計思想が根幹にあったことが大きいように思う)

そんな中1999年に発行されたC99で、C言語にも待望のBoolean型である「_Bool」が追加された。
本当ならC++と同じ型名の「bool」を追加したかったのだろうが、この時点ですでに多くのCプログラム上にdefineやtypedefされた「bool」が散在していたし、「bool」という名の変数や関数を作っているコードもあったため、それらのプログラムがビルドできなくなってしまうリスクがあって追加できなかったようだ。(同じような理由で64bit値の型名も別名で追加されることなく「long」を2つ繋げただけの「long long」となった)

一度広く普及した言語に対して、後から予約語を増やすというのは不可能に近いほど難しい。
そこで標準Cでは予め「予約済み識別子」という、プログラムを記述する際の決め事を作っていた。
これは世界中のCプログラマーに「将来予約語になる可能性があるから、このような記述は避けるようにしてね」と周知するためのものだ。
予約済み識別子にはアンダースコア+英大文字から始まるもの(「_Abcd」など)と、アンダースコア2連続から始まるもの(「__abcd」など)、それにグローバルスコープを持ちつつアンダースコアから始まるもの(「_abcd」という広域変数など)がある。
もちろんコンパイラ側で制限がかけられているものではないので、この決まりを守らない(というか予約済み識別子自体を知らない)プログラマーもたくさんいて、そういう人が書いたコードはある日突然ビルドが通らなくなる日が来る可能性があるわけだが、現在でも標準Cではそういう決まりになっている。

つまり「_Bool」はこの「予約済み識別子」を使って追加されている。
取って付けたような型名になっているのはそのためだ。
ただ、予約語として追加されたのは「_Bool」という型名だけであり、「_True」や「_False」といった値名は追加されていない。
なので_Bool型がとれる値は、数値の「1」と「0」になる。
ここがC++とはまったく違うところだ。
さすがにこれでは使いにくいと思ったのか、stdbool.hをインクルードすると「true」「false」が使えるようになり、型名も「bool」が使える。
とはいえこの中身は下のようにただdefineしているだけだ。

#define bool _Bool
#define true 1
#define false 0

このような実装から「なんだよただのマクロじゃん、中身は今までと同じintなんじゃないか」と思われがちだが、そうでもない。

#include <stdio.h>
int main(void){
  _Bool b;
  printf("%d\n", sizeof(b)); // => 1

  b = 0;
  printf("%d\n", b); // => 0

  b = 1;
  printf("%d\n", b); // => 1

  b = 256;
  printf("%d\n", b); // => 1

  b = -1;
  printf("%d\n", b); // => 1
}

MinGW-w64での検証結果だが、_Boolのサイズは1バイトであり、0以外の数値を代入しようとしてもすべて1に丸め込まれる。
これは同じMinGW-w64のC++コンパイラで、bool型相手に行ったときとまったく同じサイズと挙動だ。
つまり少なくともこのコンパイラにおいては、C言語の_Bool型は「true / false」という値名こそ無いものの、C++のbool型と同等だと見て良いのではないかと思う。
ちなみに_Bool型のサイズに関しては特に決められておらず環境依存なので、4バイト消費するコンパイラはあるかもしれないが、とれる値は0か1のどちらかだと決められている。

完全に余談。
C言語では、char, signed / unsigned char, signed / unsigned int, signed / unsigned long, signed / unsigned long long, float, double の11種類すべての型で真偽判定すると、値が0または0.0ならばfalse、0以外であればtrueとなる。


[ 戻る ]
saluteweb