C标准是否将真值明确表示为0或1?


86

我们知道,任何不等于的数字0都被视为trueC,因此我们可以这样写:

int a = 16;

while (a--)
    printf("%d\n", a);  // prints numbers from 15 to 0

但是,我想知道是否在C中将true / false定义为1/ 0,所以我尝试了以下代码:

printf("True = %d, False = %d\n", (0 == 0), (0 != 0));  // prints: True = 1, False = 0

不C标准明确指示的真正的真值和假的10分别?


3
我想这个问题很重要
Imran Ali

3
我尝试使用来运行它gcc-std=c89并且产生相同的结果。
凯文·董

1
@Blackhole,从15至0。
阿图罗托雷斯桑切斯

6
几乎是骗人的,但在SO / SE之前已有十多年了:c-faq.com/bool/bool2.html
dave_thompson_085 '16

1
假是零是正确的准则,但是通常认为真是“非零”。但是,程序员就是他们,由于各种原因,我们都将1用作“非零”。鼓励您不要相信真实的1。虽然在示例中(0 == 0)是1,但是类似(12 == 12)的值也很容易为12;也“真实”。
工程师

Answers:


96

请问C标准明确指示的真值true,并false01分别?

C标准将true和定义false为分别stdbool.h扩展到1和的宏0

C11-§7.18:

其余三个宏适用于#if预处理指令。他们是

true

扩展为整数常量1

false

扩展为整数常量0[...]

对于运营商==!=,标准表示

C11-§6.5.9/ 3:

==(等于)和!=(不等于)运算符类似于关系运算符除了他们的优先级低。108)1如果指定的关系为真,0并且为假,则每个运算符都会屈服。结果具有类型int。对于任何一对操作数,确切的关系都是正确的。


20
在我看来,这个问题是关于0 == 00 != 0等,宏不是值。
MM

9
我认为当他写文章时,true他的意思是“真正比较的价值”之类的东西,而不是宏true
MM

1
@KevinDong; 是的,C99草案有类似的段落。

1
@haccks:您可以毫不犹豫地说“相同”,因为我懒得按段查找,因为我在需要时会引用c99,所以我只想引用您的报价。而且我只能通过ctrl+ f;)搜索来找到它
-dhein

2
@MooingDuck:NaN == NaN是错误的,NaN != NaN是真实的。该语句没有问题。
kennytm '16

51

C11中未明确指出。所有语言级别的操作都将返回1作为真实值(并接受包括NaN在内的任何非零值作为true)。

  • 如果您担心_Bool,则true必须为1,因为标准仅要求其保留0和1(第6.2.5 / 2节)。
  • 还在<stdbool.h>宏中true扩展为1(§7.18/ 3)
  • ==!=<><=>=返回0或1(§6.5.8/ 6,§6.5.9/ 3)。
  • !&&||返回0或1(第6.5.3.3/5节,第6.5.13 / 3节,第6.5.14 / 3节)
  • defined 扩展为0或1(§6.10.1/ 1)

但是所有标准库函数,例如islower,对于真实性仅说“非零”(例如§7.4.1/ 1,§7.17.5.1/ 3,§7.30.2.1/ 1,§7.30.2.2.1/ 4)。


§6.2.5/ 2:声明为type的对象_Bool足够大,可以存储值0和1。

§6.5.5.3/ 5:如果逻辑运算符的操作!数值不等于0,则逻辑取反运算符的结果为0;如果运算符的值等于0,则结果为1。

§6.5.8/ 6:如果指定的关系为真,则每个运算符<(小于),>(大于),<=(小于或等于)和>=(大于或等于)应产生1,如果产生,则为0是假的。107)…

§6.5.9/ 3:(==等于)和!=(不等于)运算符与关系运算符类似,不同之处在于它们的优先级较低。108)如果指定的关系为true,则每个运算符都会产生1,如果指定的关系为true,则产生0。假。…

§6.5.13/ 3&&如果两个操作数的总和不等于0,则运算符应产生1;否则,运算符应产生1。…

§6.5.14/ 3:如果||运算符的两个操作数都不等于0,则运算符应产生1;否则,运算符应产生1。…

§6.10.1/ 1:...可能包含格式为defined identifier-—或—的一元运算符表达式,defined ( identifier )如果… ,该表达式的值为1

§7.4.1(字符分类函数)/ 1:当且仅当……时,本节中的函数返回非零(true)。

§7.18/ 3:其余三个宏适用于#if预处理指令。它们是— true—扩展为整数常量1,…

§7.17.5.1/ 3atomic_is_lock_free当且仅当对象的操作是无锁的时,泛型函数才返回非零(true)。…

§7.30.2.1(宽字符分类函数)/ 1:只有在以下情况下,本节中的函数才返回非零(true):

§7.30.2.2.1/ 4iswctype仅当以下情况时,该函数返回非零(true):


23

在C语言中处理布尔值(我的意思是对/错值,而不是特定的Cbool/_Bool类型)时,需要了解标准的两个方面。

第一个与表达式的结果有关,并且可以在C11 6.5 Expressions(例如关系和相等运算符)的各个部分中找到。最重要的是,每当表达式生成布尔值时,它都会...

...如果指定的关系为true,则产生1,如果为false,则产生0。结果的类型为int。

因此,是的,任何布尔生成表达式的结果将为true表示为1,否则为false。这与您stdbool.h在标准宏的位置找到的内容相同truefalse并且以相同的方式定义。

但是请记住,遵循“在发送内容时保持保守,在接收的内容中保持自由”的鲁棒性原则,在布尔上下文中对整数的解释会稍微宽松一些。

同样,在的各个部分6.5,您将看到类似以下的语言:

如果||运算符的两个操作数都不等于0,则运算符应产生1;否则,运算符应产生1 否则,结果为0。结果的类型为int。

从这一点(以及其他部分)来看,很明显零被认为是错误的,而其他任何值都是正确的。


顺便说一句,指定用于布尔值生成和解释的值的语言也出现在C99和C89中,因此它们已经存在了一段时间。甚至K&R(ANSI-C第二版第一版)也使用以下文本段进行了指定:

i > j&&和连接的关系表达式(如和逻辑表达式)||被定义为1在true和0false时具有值。

在测试部分ifwhilefor,等,“真”只是意味着“非零”。

如果&&运算符...的两个操作数的总和不等于零,则返回1,否则返回0。

如果||运算符的操作数比较不等于零,则返回1,否则返回0。

中的宏也将stdbool.h出现在C99中,但不会出现在C89或K&R中,因为那时该头文件不存在。


2
注意||==!=等产量int,而不是一个boolean类型
MM

2
我对这个问题投赞成票。对我来说,问题还关乎关系运算符,而不是宏。
ckruczek '16

“在测试部分ifwhilefor等,‘真’只是意味着‘非零’。” 这是答案的重要部分,我认为这是很久以前丹尼斯·里奇的不幸选择。任何编写了将错误代码作为返回值返回的函数的人,通常都具有#define noErr 0并且任何非零错误代码都是错误。然后问题是简单性和美观性if ( ready_to_do_something() ){do_something();} 无法正常工作。它必须是if ( !not_ready_to_do_something() ){do_something();}“有许多谎言,但只有一个真理”。TRUE应该为
0。–罗伯特·布里斯托

出于好奇,C规则的初稿如何指定“ &&”和“ ||”的行为 如果操作数的值不是0或1?您引用的文字说的是由&&和||连接的“逻辑表达式”,但是如果这些运算符连接了逻辑表达式以外的东西怎么办?
超级猫

1
@sdenham,是的。在我拥有的K&R的最早版本中(第一版,印刷版14,发行得如此之早,以至于提到了四种典型机器的硬件特性,即PDP-11,Honeywell-6000,IBM-370和Interdata-8 / 32),A.7.6/7/10/11(关系/等式/逻辑和/或逻辑或)均指定结果为0或1。更新了答案以包括该答案。
paxdiablo

10

您正在混淆很多不同的东西:控制语句,运算符和布尔类型。每个都有自己的规则。

控制语句的工作方式类似于ifC11 6.4.8.1语句:

在这两种形式中,如果表达式比较不等于0,则执行第一个子语句。

whilefor等等具有相同的规则。这与“ true”或“ false”无关。

对于应该产生布尔结果的运算符,它们实际上产生的int值为1或0。例如,等于运算符C11 6.5.9:

如果指定的关系为true,则每个运算符的结果为1;如果为false,则为0。

上面所有这些都是因为C直到1999年才有了布尔类型,即使C确实得到了布尔类型,上述规则也没有改变。因此,与大多数其他编程语言(其中的语句和运算符产生布尔类型(例如C ++和Java))不同,它们只是产生一个int,其值为零或非零。例如,sizeof(1==1)在C中给出4,而在C ++中给出1。

C语言中的实际布尔类型已命名,_Bool并且需要现代编译器。头stdbool.h定义的宏booltruefalse展开后_Bool10分别(用于C ++兼容性)。


但是,将控制语句和操作符视为实际需要/产生布尔类型被认为是良好的编程习惯。某些编码标准(例如MISRA-C)推荐这种做法。那是:

if(ptr == NULL)代替if(ptr)

if((data & mask) != 0)代替if(data & mask)

这种样式的目的是借助静态分析工具提高类型安全性,从而减少错误。可以说,只有当您使用静态分析器时,这种样式才有意义。尽管在某些情况下,它会导致更具可读性的自我记录代码,例如

if(c == '\0') 

好的,目的很明确,代码是自我记录的。

if(c) 

坏。可能有任何含义,我们必须去寻找c能够理解代码的类型。它是整数,指针还是字符?


1
sizeof(bool)是特定于C ++的实现。参见stackoverflow.com/questions/4897844/is-sizeofbool-defined
大卫·哈门

@DavidHammen正如sizeof(0 == 0)也是实现定义的。这只是一个例子。
伦丁

我以为C确实改变了布尔类型的规则。其他类型的uintN类型(包括许多较早的编译器的“位”类型)存储值的低N位并忽略任何高位,而新的布尔类型有效地将所有位“或”在一起。
超级猫

1
如果是这样if(ptr != NULL),或者是if(!ptr)
Mathieu K.

1
if(c == '\0')很容易引起初学者的编码错误if(c = '\0'),所以我避免了。同意,这if(c)很糟糕...例如,应该是if(valveIsOpen)
2016年

4

我已经用多种语言编程。我已经看到true取决于语言是1还是-1。逻辑为1的逻辑是某个位是0或1。逻辑为-1的逻辑是!运算符是一个补充。它在一个int中将所有1更改为0,并将所有0更改为1。因此,对于一个int来说,!0 = -1和!(-1)=0。这使我感到震惊,以至于我不比较== true的东西,而是比较它等于!= false的东西。这样,我的编程风格便可以在每种语言中使用。因此,我的答案是不必担心它,而要编程,以便您的代码正确地以任何一种方式工作。


怎么能 !将所有0更改为1,并且仍然对!5产生0?
截图

@codeshot不能。但是他要说的是,并非所有语言都将!的操作数作为对待对象!作为布尔值。一些请客!如C的〜-即按位补码。在这种情况下,确定结果值首先需要知道变量的类型,所以!(uint32_t)5将为4,294,967,290。但是!0仍然是4,294,967,295,并且4,294,967,295是真实的。
飞马Epsilon

1

这个答案需要更仔细地研究。

C ++中的实际定义是将任何非0的值都视为true。为什么这有关系?因为C ++不了解整数是什么-我们创建了这个含义,所以它所拥有的只是shell和表示该含义的规则。它知道什么位,组成整数。

1作为整数可以用位粗略地表示,例如8位带符号的int表示为00000001。在很多情况下,我们在视觉上看到的都是谎言,由于符号的性质,-1是更常见的表示方式的“整数”。1真的不能代表真正的正确,为什么?因为不是运算符,所以是11111110。这对于布尔值来说确实是一个重大问题。当我们谈论布尔值时,它只有1位,这很简单,0为假,1为真。所有逻辑运算都很简单。这就是为什么整数(带符号)应将“ -1”指定为“ true”。1111 1111 NOT'ed变为0000 0000 ---逻辑成立,我们很好。无符号整数有点棘手,在过去更常用-其中1表示正确,因为很容易暗示逻辑“

这就是解释。我说这里接受的答案是错误的-C / C ++定义中没有明确的定义。布尔值是布尔值,您可以将整数视为布尔值,但是输出是整数这一事实并不能说明实际执行的操作是按位的。


4
问题是关于C,而不是C ++。
glglgl

0

发生这种情况是因为您的printf陈述中有关系运算符。

操作员==和操作员!=

由于(0 == 0)成立的话,它给出的值1

然而,(0 != 0)不成立,给出了一个值0

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.