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

我们知道任何不等于 0的数字在 C 语言中都被看作是 true,所以我们可以写:

int a = 16;


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

然而,我想知道 true/false 是否在 C 语言中定义为 1/0,所以我尝试了下面的代码:

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

C 标准是否分别以 10表示 true 和 false 的真值?

41855 次浏览

It happened because of the Relational Operators in your printf statement.

Operator == and operator !=

Since (0 == 0) holds true so, it gives a value 1

whereas, (0 != 0) doesn't hold true so, gives a value 0 .

Does the C standard explicitly indicate the truth values of true and false as 0 and 1 respectively?

The C standard defines true and false as macros in stdbool.h which expand to 1 and 0 respectively.

C11-§7.18:

The remaining three macros are suitable for use in #if preprocessing directives. They are

true

which expands to the integer constant 1,

false

which expands to the integer constant 0 [...]

For the operators == and != , standard says

C11-§6.5.9/3:

The == (equal to) and != (not equal to) operators are analogous to the relational operators except for their lower precedence.108) Each of the operators yields 1 if the specified relation is true and 0 if it is false. The result has type int. For any pair of operands, exactly one of the relations is true.

There are two areas of the standard you need to be aware with when dealing with Boolean values (by which I mean true/false values rather than the specific C bool/_Bool type) in C.

The first has to do with the result of expressions and can be found in various portions of C11 6.5 Expressions (relational and equality operators, for example) . The bottom line is that, whenever a Boolean value is generated by an expression, it ...

... yields 1 if the specified relation is true and 0 if it is false. The result has type int.

So, yes, the result of any Boolean-generating expression will be one for true, or zero for false. This matches what you will find in stdbool.h where the standard macros true and false are defined the same way.

Keep in mind however that, following the robustness principle of "be conservative in what you send, liberal in what you accept", the interpretation of integers in the Boolean context is somewhat more relaxed.

Again, from various parts of 6.5, you'll see language like:

The || operator shall yield 1 if either of its operands compare unequal to 0; otherwise, it yields 0. The result has type int.

From that (and other parts), it's obvious that zero is considered false and any other value is true.


As an aside, the language specifying what value are used for Boolean generation and interpretation also appear back in C99 and C89 so they've been around for quite some time. Even K&R (ANSI-C second edition and the first edition) specified that, with text segments such as:

Relational expressions like i > j and logical expressions connected by && and || are defined to have value 1 if true, and 0 if false.

In the test part of if, while, for, etc, "true" just means "non-zero".

The && operator ... returns 1 if both its operands compare unequal to zero, 0 otherwise.

The || operator ... returns 1 if either its operands compare unequal to zero, and 0 otherwise.

The macros in stdbool.h appear back in C99 as well, but not in C89 or K&R since that header file did not exist at that point.

It is not explicitly indicated in C11. All language-level operations will return 1 as truthy (and accept any nonzero including NaN as true).

  • If you concern about _Bool, then true must be 1 because the standard only require it to hold 0 and 1. (§6.2.5/2).
  • Also in <stdbool.h> the macro true expands to 1 (§7.18/3)
  • ==, !=, <, >, <= and >= return 0 or 1 (§6.5.8/6, §6.5.9/3).
  • !, && and || return 0 or 1 (§6.5.3.3/5, §6.5.13/3, §6.5.14/3)
  • defined expands to 0 or 1 (§6.10.1/1)

But all standard library functions e.g. islower just say "nonzero" for truthy (e.g. §7.4.1/1, §7.17.5.1/3, §7.30.2.1/1, §7.30.2.2.1/4).


§6.2.5/2: An object declared as type _Bool is large enough to store the values 0 and 1.

§6.5.5.3/5: The result of the logical negation operator ! is 0 if the value of its operand compares unequal to 0, 1 if the value of its operand compares equal to 0. …

§6.5.8/6: Each of the operators < (less than), > (greater than), <= (less than or equal to), and >= (greater than or equal to) shall yield 1 if the specified relation is true and 0 if it is false.107) …

§6.5.9/3: The == (equal to) and != (not equal to) operators are analogous to the relational operators except for their lower precedence.108) Each of the operators yields 1 if the specified relation is true and 0 if it is false. …

§6.5.13/3: The && operator shall yield 1 if both of its operands compare unequal to 0; …

§6.5.14/3: The || operator shall yield 1 if either of its operands compare unequal to 0; …

§6.10.1/1: … it may contain unary operator expressions of the form — defined identifier — or — defined ( identifier ) — which evaluate to 1 if …

§7.4.1 (Character classification functions)/1: The functions in this subclause return nonzero (true) if and only if …

§7.18/3: The remaining three macros are suitable for use in #if preprocessing directives. They are — true — which expands to the integer constant 1, …

§7.17.5.1/3: The atomic_is_lock_free generic function returns nonzero (true) if and only if the object’s operations are lock-free. …

§7.30.2.1 (Wide character classification functions)/1: The functions in this subclause return nonzero (true) if and only if …

§7.30.2.2.1/4: The iswctype function returns nonzero (true) if and only if …

You are mixing up a lot of different things: control statements, operators and boolean types. Each have their own rules.

Control statements work like for example the if statement, C11 6.4.8.1:

In both forms, the first substatement is executed if the expression compares unequal to 0.

while, for etc have the same rule. This has nothing to do with "true" or "false".

As for operators that are supposedly yielding a boolean result, they are actually yielding an int with value 1 or 0. For example the equality operators, C11 6.5.9:

Each of the operators yields 1 if the specified relation is true and 0 if it is false

All of the above is because C did not have a boolean type until the year 1999, and even when it did get one, the above rules weren't changed. So unlike most other programming languages where statements and operators yield a boolean type (like C++ and Java), they just yield an int, with a value zero or not zero. For example, sizeof(1==1) will give 4 in C but 1 in C++.

The actual boolean type in C is named _Bool and requires a modern compiler. The header stdbool.h defines macros bool, true and false, that expand to _Bool, 1 and 0 respectively (for compatibility with C++).


It is however considered good programming practice to treat control statements and operators as if they actually required/yielded a boolean type. Certain coding standards like MISRA-C recommend such practice. That is:

if(ptr == NULL) instead of if(ptr).

if((data & mask) != 0) instead of if(data & mask).

The aim of such style is to increase type safety with the aid of static analysis tools, which in turn reduces bugs. Arguably, this style is only meaningful if you do use static analysers. Though in some cases it leads to more readable, self-documenting code, for example

if(c == '\0')

Good, the intent is clear, the code is self-documenting.

versus

if(c)

Bad. Could mean anything, and we have to go look for the type of c to understand the code. Is it an integer, a pointer or a character?

I've programmed in many languages. I've seen true be 1 or -1 depending on the language. The logic behind true being 1 was that a bit was either a 0 or 1. The logic behind true being -1 was that the ! operator was a one's complement. It changed all the 1's to 0's and all the 0's to 1's in an int. So, for an int, !0 = -1 and !(-1) = 0. This has tripped me up enough that I don't compare something to be == true, but instead compare it to be != false. That way, my programming style works in every language. So my answer is to not worry about it, but program so that your code works correctly either way.

This answer needs to be looked at a bit more closely.

The actual definition in C++ is that anything not 0 is treated as true. Why is this relevant? Because C++ doesn't know what an integer is by how we think about it--we create that meaning, all it holds is the shell and rules for what that means. It knows what bits are though, that which make up an integer.

1 as an integer is loosely represented in bits, say an 8-bit signed int as 0000 0001. Many times what we see visually is a bit of a lie, -1 is a much more common way to represent it because of the signed nature of 'integer'. 1 really can't mean true proper, why? Because it's NOT operation is 1111 1110. That's a really major issue for a boolean. When we talk about a boolean, it's just 1 bit--it's really simple, 0 is false and 1 is true. All the logic operations hold as trivial. This is why '-1' should be designated as 'true' for integers (signed). 1111 1111 NOT'ed becomes 0000 0000---the logic holds and we're good. Unsigned ints is a little bit tricky and were a lot more commonly used in the past--where 1 means true because it's easy to imply the logic that 'anything not 0 is true'.

That's the explanation. I say the accepted answer here is wrong--there is no clear definition in the C/C++ definition. A boolean is a boolean, you can treat an integer as a boolean, but the fact the output is an integer says nothing about the operation actually being done is bitwise.

I think I might have found the perfect solution to your problem. Yes, 0 and any non-zero number are False and True respectively. Though there is no boolean data type in C.

But this is not the problem, the actual problem is how you are dealing with the modification of variable a in the your code :

int a = 16;


while (a--){
printf("%d\n", a);
}

When the compiler comes to the while (condition) statement, first the value of a is read by the compiler for the condition, then the arithmetic operation takes place, in this case,
a = a - 1 / a -= 1. So in the end there will be a case when a = 1 and the condition satisfies and after the arithmetic operation a-- which leads to a = 0, the print statement prints a as 0.

The above scenario depends on whether you use --a or a--. These two statements are read by the compiler in the order they are written. For --a first the operation is performed on a then its value is read and vice-versa for the other.

So for case --a when a = 1 first the operation is done i.e a = a - 1 / a -= 1 and then a is evaluated for the condition, which then comes out to be Falsy as a = 0. Try the code below :

int a = 16;


while (--a){
printf("%d\n", a);   // prints numbers from 15 to 1 as intended
}

OR deal with the modification of a inside the while loop block.

int a = 16;


while(a){
a = a - 1;           // or a -= 1
printf("%d\n", a);   // also prints numbers from 15 to 1 as intended
}