任何一个C编译器中“==”会大于一吗?

11

由于任何非零值都表示为真,但是><==等运算符返回1表示真,我想知道是否有任何重要的C编译器在这些运算符可以产生大于1的值。

换句话说,是否有任何编译器在执行 int i = (a == b); 这个语句时,如果我打算将 i 用作整数而不是布尔值,并假设它将是01,则会导致未定义的行为?


3
那将是一个显著的编译器。 - Michael Burr
2
s/notable/<插入脏话>/g :-) - paxdiablo
这只是一个“思想实验”问题,还是你试图编写能够在任何已经编写的C编译器上编译的代码?如果你担心它可能不起作用,只需反转你的比较。false始终为0。 - Neil
@Neil:我写了一段代码,应该可以在1997年左右的编译器上编译通过,为了确保这一点,我使用了?1:0来进行“布尔化”,但我想知道我是否真的需要如此谨慎。 - vsz
7个回答

19

不会,如果有的话,它们不是C编译器:) C语言规定关系和相等运算符返回1表示真,0表示假。


C语言对将整数值解释为布尔值的规则是,0表示假,任何其他值表示真。请参见处理 if / while / do / for 的C11部分,其中所有内容都包含类似于"while表达式与零不相等"的语言。具体地:

6.8.4.1/2:在两种形式[if语句的一种有else子句,一种没有]中,如果表达式与0不相等,则执行第一个子语句。在else形式中,如果表达式比较等于0,则执行第二个子语句。

6.8.5/4:迭代语句[while、do和for]导致称为循环主体的语句重复执行,直到控制表达式与0相等。


然而,针对比较类型的表达式,你将得到什么结果就非常明确了,你要么得到0,要么得到1。这些方面的C11标准的相关部分全部都在6.5表达式下:

6.5.8/6:<(小于)、>(大于)、<=(小于或等于)和> =(大于或等于)运算符中的每个运算符都应返回1,如果指定的关系为真,则返回0。

6.5.9/3:==(等于)和!=(不等于)运算符类似于关系运算符,除了它们的优先级较低之外。如果指定的关系为真,则每个运算符都返回1;如果指定的关系为假,则返回0。

6.5.13/3:&&运算符应返回1,如果它的两个操作数都与0不相等;否则,它将返回0。

6.5.14/3: || 运算符应当在它的任意一个操作数与 0 不相等时返回 1;否则,返回 0。

6.5.3.3/5: ! 运算符对于操作数值与 0 不相等时返回 0,如果操作数等于 0,则返回 1。

这种行为早在 C99 和 C89(ANSI 时代)就已经存在了。C99 中处理关系和等式运算符的章节也指出其返回值只能是 0 或 1。

C89 草案虽然没有明确规定等式运算符的返回值,但它确实说:

==(等于)和!=(不等于)运算符与关系运算符相似,只是它们的优先级较低。

而关系运算符一节确实说明:

<(小于)、>(大于)、<=(小于或等于)和>=(大于或等于)运算符应当在指定的关系为真时返回 1,为假时返回 0。

参考:http://flash-gordon.me.uk/ansi.c.txt,因为我没有任何 C89 标准的副本。我有第二版的 K&R(1988 年 ANSI 版本),它在附录 A 的第 7.9 和 7.10 节中基本上说明了同样的内容。如果您想要从第一版得出确定的答案,那就需要找一个妻子不太喜欢扔旧东西的人。


补充:

根据 Michael Burr 的说法,他要么没有结婚,要么他的妻子比我更愿意保留旧书 :-)

K&R第一版(1978年)在7.6和7.7中也是这样说的:“如果指定的关系为假,则所有[关系运算符]均产生0,如果为真,则产生1。” …“[等式运算符]与关系运算符完全类似,只是它们的优先级较低。”


1
@Als:现在几乎所有的编译器都是C99,除了一些制作于1997年的编译器,它们仍然用于特定的嵌入式处理器(是的,即使是新的),但手册中没有指明它们基于哪个标准(我认为不会比C89更旧)。 - vsz
@vsz,实际上,根据我所见过的 ANSI C89 标准草案(http://flash-gordon.me.uk/ansi.c.txt),要求返回 1 或 0 对符合 ANSI 标准的编译器也是适用的。因此,除非你的编译器早于那个时期,否则可以安全地假定为 1/0。如果它确实早于那个时期,你可能需要考虑加入我们进入21世纪 :-) - paxdiablo
1
当你在输入那个评论时,@vsz已经完成了。 - paxdiablo
2
K&R第一版(1978)在7.6和7.7中也说了同样的话:“如果指定的关系为假,则[关系运算符]都产生0,如果为真,则产生1。”...“[相等运算符]与关系运算符完全类似,只是它们的优先级较低。”因此,我认为这种行为对于除了奇怪的C编译器之外的所有人来说已经存在很长时间了。请记住,在算术表达式中使用关系或相等运算符的结果,例如a +=(b == c)可能不是每天都会看到,但它们足够常见。它们在过去可能更加普遍。 - Michael Burr
谢谢,@Michael,我会将其添加到答案中。 - paxdiablo
显示剩余7条评论

9
它们保证返回01

参考资料:
C99标准:6.5.9 相等运算符

第3段:

==(等于)和!=(不等于)运算符类似于关系运算符,除了它们的优先级较低外。108)如果指定的关系为真,则每个运算符产生1,如果为假则产生0。结果的类型为int。对于任何一对操作数,只有一个关系为真。


如果编译器不符合标准,则可能是任何返回值,但标准规定返回值应为01,编译器实现应遵守标准规定。 - Alok Save

4

我认为这里的人们并没有回答问题。问题不是关于标准规定了什么,而是是否有任何著名的编译器在这方面不遵守标准。

据我所知,没有这样的编译器。


无论标准如何,总有人不会完全遵循,就像看看CSS一样。但是,我希望没有人会违反这么简单的事情。 - vsz
是的,正如我所说。K&R C(ANSI之前)对比较运算符产生的值范围没有限制(除了“false == zero”)。 - wildplasser
@wildplasser: 这并非没有道理。如果只需返回非零值而不必始终将值设置为1,则关系运算符可能更快。这就是为什么我认为对于低端微控制器的旧编译器采用这种捷径并非完全不可能。 - vsz
我认为“更快”不是一个目标。仍然存在“好像”规则,在大多数情况下,布尔值仅用于其真值(因此适用“好像”规则)。在算术表达式中使用布尔值非常罕见,而在这种情况下,三元运算符会更加明确。我不知道c89决定的确切理由是什么,可能是一些模糊的东西,比如“整理”或“与C ++保持一致”。 - wildplasser

3

按照规范,为了被视为C语言,条件运算符必须返回0表示false,返回1表示true。

用俳句的形式表达:

条件成立, 回归1; 不成立, 安放0。
Specification
Disallows this behavior,
Otherwise not C.

3
根据维基百科(Wikipedia)显示,我无法访问C标准:
C使用一种整数类型,其中类似i>j的关系表达式和由&&和||连接的逻辑表达式在为真时定义为值1,为假时定义为值0,而if,while,for等的测试部分将任何非零值视为true。幸运的是,Wikipedia确实有正确的引用,但是因为它们是对书籍的引用,所以不太确定能提供多少帮助 :).

我知道这个,我只是想确保没有任何异常情况。 - vsz
如果标准规定了这一点,那么就不会有编译器违反如此简单的规定(实际上被使用)。 - Corbin

3
据我所知,这段内容来自C99标准中第6.5.9段的第3部分。
引用如下:

等于(==)和不等于(!=)运算符类似于关系运算符,除了它们具有更低的优先级。108)每个运算符如果指定的关系成立,则产生1;否则产生0。结果类型为int。对于任何一对操作数,只有一个关系是真的。

因此,看起来计算出的值应该是1或0中的一个

2
在c89/c90/ANSI-C之前,比较运算符保证在“false”条件下产生零值,在不等于零的情况下产生非零值。c89引入了“将1替换为true”的“标准化”,但这种情况很少需要,在需要的情况下,可以使用a = (b==c) ? 1 : 0;。

2
我不确定说“将1替换为true的标准化”是由c89引入的是否正确。K&R的1978版清楚地说明了关系和相等运算符的0/1行为。我猜(这只是一个猜测),那个规范通常被遵循而不是被忽略。当然,它可能有时未被遵循,但这个规则对于C89来说并不是新的。 - Michael Burr
可能一直在演变的做法。我想我们的工作某个地方仍然使用K&R1。(但是如果我没记错,K&R1从未制定过“标准”,只是描述了一种语言)。我会尝试在周一核实一下。K&R2略早于c89 / c90,但我记得的那本书封面上有一些“特性ANSI C”的标志。可能是第二版的再版。 - wildplasser

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接