我刚刚偶然发现一个 assert 失败了,因为它将false与函数的returntype进行了比较,因为函数本身返回了一个bool,而assert不仅检查了值,而且还检查了返回值的类型是否与false相匹配,以保证返回bool。
现在的问题是C99将bool定义为_Bool,而_Bool甚至不一定与int大小相同(事实上,根据我的经验,在现在的大多数平台上,它的大小往往与 unsigned char 相同),更不用说是同一类型了(这实际上是不可能的,因为_Bool是C99语言的内置类型),但是将false和true定义为0和1而没有任何类型转换,并且没有类型转换的preprocessor定义将默认为int。
如果C99将false和true定义为((bool)0)和((bool)1),则它们将始终是bool类型,无论_Bool是如何定义的。
那么,是否有一个很好的理由让它们总是被定义为int,即使bool在那个平台上不是int,或者这只是语言中的一个bug,应该用C1x来修复?
4条答案
按热度按时间tvokkenx1#
false
和true
分别被定义为整数常量0
和1
,因为这正是C99标准在第7.16节中所规定的:< SNIP >
其余三个宏适合在**#if**预处理指令中使用。是的
true
其扩展为整数常数1,
错误
其扩展为整数常数0,并且
< SNIP >
true
和false
应该可以在#if
预处理指令中使用(正如标准中提到的那样)。((bool) 0)
或((bool) 1)
在#if
预处理指令中不起作用的原因是因为标准不允许它。在第6.10.1节 * 中,其表示:控制条件包含的表达式应为整数常量表达式,但以下情况除外:不得有铸件;
jobtbby32#
除了已经提到的其他原因之外,因为只要您对
_Bool
执行几乎任何操作,它就会变成int
。例如,
(_Bool)0 & (_Bool)1
的类型是什么?你可能认为这个表达式的类型是_Bool
,但实际上§6.5.10定义了&
的语义:...通常的算术转换是在操作数上进行的...
“通常的算术转换”在C标准中有非常特殊的含义。它在§6.3.1.8中定义,包括以下内容:
...整数提升在两个操作数上执行...
“整数促销”也是§ 6.3.1.1中定义的术语:
如果int可以表示原始类型的所有值,则该值将转换为int;否则,它将被转换为unsigned int。这些被称为整数提升。48)所有其他类型都不会因整数提升而改变。
尽管在C标准中存在比
int
更窄的类型,但在几乎任何表达式中,它们都会自动扩展为int
。再加上布尔运算的结果具有int
类型,这使得int
成为这些文字类型的自然选择。wkyowqbh3#
所有其他的答案都试图用标准来证明为什么标准以某种方式定义事物,我觉得这并不令人满意。标准不仅定义了类型,还定义了运算符和预处理器,所以如果C99引入了布尔类型,为什么不改变所有的布尔运算符来计算该类型的值,并扩展预处理器以支持布尔类型呢?
这样做是可能的,但更复杂的必要。对于标准编写者和编译器编写者来说,只做最小的必要修改来向语言中添加新的布尔类型要容易得多。由于所有的布尔运算仍然计算为
int
类型,所有的前C99编译器都可以更新为支持C99,而不必更改所有基本运算符的类型计算代码,并且标准编写者可以更有信心,新的布尔特性不会意外地将不一致引入到以前很好的标准部分。他们所需要做的就是确保“通常的算术转换”应用于_Bool
,然后保证其他一切都可以工作。这不是技术原因。这是一个实际的问题。
n7taea2i4#
首先,虽然
_Bool
可能不是int
,但要求_Bool
可以接受值0和1,因此将true
和false
扩展为1和0是可以的。此外,为了向后兼容,
true
和false
是合理的int
s,因为所有逻辑运算符都返回int
。!
的操作数的值比较不等于0,则其结果为0;如果运算符!
的操作数的值比较等于0,则其结果为1。结果的类型为int
。表达式!E
等价于(0==E)
。<
(小于)、>
(大于)、<=
(小于或等于)和>=
(大于或等于)如果指定的关系为真,则产生1,如果为假,则产生0。==
(等于)和!=
(不等于)运算符类似于关系运算符,只是它们的优先级较低。91)如果指定的关系为真,则每个运算符都产生1,如果指定的关系为假,则产生0。结果的类型为int
。对于任何一对操作数,只有一个关系为真。&&
运算符的两个操作数比较都不等于0,则运算符将产生1;否则,它产生0。结果的类型为int
。||
运算符的任一操作数比较不等于0,则运算符将产生1;否则,它产生0。结果的类型为int
。最后,正如@Sander De Dycker提到的,标准定义的
true
和false
可以像这样扩展(C99 § 7.16/3)。