bool型はCで規定されていたのですね

linuxのコマンドのソース*1を眺めていたらbool型を使っているのを見つけました.
それまでboolはユーザレベルでenumを用いたものをtypedefしているものだと思っていました.

おかしいなと思ったのはviで見たときに色がついていたことでした.
よく使う型だから便利のためにマークアップされていることはあるのかもしれませんが,気になって一応調べてみました.

というかboolを使ったサンプルプログラムを書いてみました.

bool1.c

 1	#include <stdio.h>
 2	#include <stdlib.h>
 3	#include <stdbool.h>
 4	
 5	int 
 6	main (int argc, char *argv[])
 7	{
 8		bool is = true;
 9		int num = 0;
10	
11		puts("start!");
12		do {
13			printf("%d: %d\n", is, num);
14			++num;
15			if (num > 10) is = false;
16		} while (is);
17		puts("end!");
18	
19		return 0;
20	}

どうやらstdbool.hが必要なようです.


stdbool.hには次のようにありました.

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

この_BoolこそがC99で規定されているブーリアンの型のようです.
boolにしているのは簡単のためと,それまでの互換性のためなのでしょうか...
boolの方が自然な感じはありますが.


trueやfalseはマクロで1と0になっています.
マクロ展開はプリプロセスなので,1と0には型は無いと思うのですが,
わざとint型の整数をboolに代入したらどうなるのでしょうか.
規格書には

型_Boolとして宣言されたオブジェクトは, 値0及び1を格納するのに十分な大きさをもつ。
(中略)
型_Bool, 及び標準符号付き整数型に対応する符号無し整数型は, 標準符号無し整数型(standard unsigned integer type)とする。

http://www.amy.hi-ho.ne.jp/~lepton/program/p3/prog320.html

と書いてあるようですが*2,1より大きな値が入る余地がある気がします.
そこで上のソースにちょっと細工をしてみました.

bool2.c

 1	#include <stdio.h>
 2	#include <stdlib.h>
 3	#include <stdbool.h>
 4	
 5	int 
 6	main (int argc, char *argv[])
 7	{
 8		bool is = true;
 9		bool a = 2;
10		int num = 0;
11	
12		printf("%d\n", a);
13		puts("start!");
14		do {
15			printf("%d: %d, %d\n", is, num, (bool)num);
16			++num;
17			if (num > 10) is = false;
18		} while (is);
19		puts("end!");
20	
21		return 0;
22	}

実行結果

1
start!
1: 0, 0
1: 1, 1
1: 2, 1
1: 3, 1
1: 4, 1
1: 5, 1
1: 6, 1
1: 7, 1
1: 8, 1
1: 9, 1
1: 10, 1
end!

即値を代入してもキャストしても,結果は0か1になるみたいです.
コンパイラがbool型の変数を判別して1より大きい値は1にするようにしてるのでしょうね.

ではこんな風にしたらどうなるでしょうか.注目箇所は10行目です.

bool3.c

 1	#include <stdio.h>
 2	#include <stdlib.h>
 3	#include <stdbool.h>
 4	#include <limits.h>
 5	
 6	int 
 7	main (int argc, char *argv[])
 8	{
 9		bool is = true;
10		bool a = INT_MIN-1;
11		int num = 0;
12	
13		printf("%d\n", a);
14		puts("start!");
15		do {
16			printf("%d: %d, %d\n", is, num, (bool)num);
17			++num;
18			if (num > 10) is = false;
19		} while (is);
20		puts("end!");
21	
22		return 0;
23	}

コンパイル結果

$ gcc bool3.c -Wall
src.c: In function 'main':
src.c:10: warning: integer overflow in expression

オーバーフローしました.実行結果はboot2.cのものと同じになりました.
INT_MIN-1ではなくCHAR_MIN-1にすると,正常にコンパイルできて,bool2.cの実行結果と同じになりました.


intで確保して,後で1に落としているのでしょうか?ここは疑問が残りました.


stdbool.hを調べるときに気づいたこと

話が逸れるので別のセクションにしました.
stdbool.hはどこにあるのでしょうか.
わたしの環境では/usr/lib/gcc/i386-redhat-linux/4.1.1/includeにありました.
gccコンパイルするときのライブラリの検索パスとして,このパスが入っているようです.


ところでデフォルトでgccが参照するパスはどこなのでしょう.
env | grep -i LIBなどで検索しましたが,環境変数には特にそれらしいものがありませんでした*3
/usr/lib/gcc-lib/以下にspecsファイルというものがあり,
その中で記述されているようなのですが,私の環境ではありませんでした.
その代わりにコンパイル時の処理をverboseにしてやると出力されます*4

$ gcc -c -v src.c
Using built-in specs.
Target: i386-redhat-linux
  --snip--
#include "..." の探索はここから始まります:
#include <...> の探索はここから始まります:
 /usr/local/include
 /usr/lib/gcc/i386-redhat-linux/4.1.1/include
 /usr/include
探索リストの終わり
  --snip--

リンクまでしてしまうと画面いっぱいになってしまうかもしれないので,
cオプションが入れてあります.お好みで.


参考

http://nssearch.hp.infoseek.co.jp/clang/
http://gcc.gnu.org/onlinedocs/ -> GCC (version) Manual -> GCC Command Options -> Spec Files