C23 中的 bool
Source: Dev.to
Introduction
最初,C 语言没有布尔类型。取而代之的是使用 int,其中 0 被视为 false,任何非零值被视为 true。虽然这样可以工作,但使用 int 作为布尔值会带来若干问题。
Problems with int
使用 int 作为布尔值的一个问题是可读性。考虑下面的函数:
int strbuf_reserve(strbuf_f *sbuf, size_t res_len);
这个函数返回什么?一个简单的布尔值?实际预留的字节数?错误码?你必须阅读文档(如果有的话)或源码才能知道 int 的含义。
因此,许多程序员在 C 中自行定义布尔“类型”,例如:
#define bool int /* or: typedef int bool; */
#define false 0
#define true 1
或者使用全大写的版本。然而,当不同库对布尔类型的定义略有差异时,就会导致不兼容,产生警告或错误。
另一个问题是内存使用。在结构体中,int 至少占用四个字节(在 32 位系统上),而真正的布尔值只需要一个位,实际使用时也至少需要 sizeof(char)(始终为 1)。
_Bool in C99
C 委员会最终在 C99 中为 C 添加了布尔类型:_Bool。选择前导下划线是为了避免破坏已经把 bool 当作标识符使用的旧程序。
标准还引入了宏:
#define bool _Bool
#define false 0
#define true 1
包含此头文件后,你就可以使用传统的 bool、false、true 名称,而不会破坏旧代码。
Interaction with _Generic (C11)
当 C11 引入 _Generic 时,_Bool 的集成并不顺畅:
#include <stdio.h>
void f_bool(void) { }
void f_int(void) { }
#define F(X) \
_Generic((X), \
bool: f_bool, \
int : f_int \
)( (X) )
int main(void) {
bool b = false;
F(b); // calls f_bool
F(false); // calls f_int
}
调用 F(false) 会调用 f_int,因为 false 是一个宏,展开为 0,其类型为 int。
Casting behavior
int i1 = (int)0.5; // = 0 (truncation)
_Bool b1 = (_Bool)0.5; // = 1 (non‑zero → true)
int i2 = (int)LONG_MAX; // implementation‑defined
_Bool b2 = (_Bool)LONG_MAX; // = 1
与 int 不同,任何非零值强制转换为 _Bool 时都会隐式转换为 1(true),其语义更直观且定义明确。
bool in C23
在 C23 中,委员会终于加入了完整的布尔类型:bool。关键字 false 和 true 现在已成为语言的一部分,消除了 _Generic 与 _Bool 之间出现的问题。
理想情况下,bool 本可以在 C11 与 _Generic 同时引入,但在 C23 的加入终于提供了一个合适的、原生的布尔类型。
Conclusion
经过 _Bool 超过四分之一个世纪以及 bool 超过半个世纪的演进,C 现在拥有了一个更清晰、比 int 更小且始终定义明确的布尔类型。