5

I just stumbled across an assert that failed, as it compared false to the returntype of a function, as the function itself returned a bool and the assert checked not only the value, but also the type of the returnvalue to match the one of false, to guarantee, that a bool is returned.

Now the problem is that C99 defines bool as _Bool and _Bool is even not necessarily the same size as int (in fact, in my experience, on most platforms nowadays, it is often the same size as unsigned char), not to talk about being the same type (which is actually impossible, as _Bool is a builtin type of the language in C99), but defines false and true as 0 and 1 without any typecast and preprocessor definitions without a typecast will default to int.

If C99 would instead define false and true as ((bool)0) and ((bool)1), they would always be of type bool, no matter, how _Bool is defined.

So is there a good reason to have them always defined as ints, even when bool is not an int on that platform or is this just a bug in the language that should be fixed with C1x?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Kaiserludi
  • 2,434
  • 2
  • 23
  • 41

4 Answers4

9

false and true are defined as the integer constants 0 and 1 respectively, because that's exactly what the C99 standard specifies in section 7.16 :

< SNIP >

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, and

< SNIP >

EDIT : as the comments below indicate, it seems I slightly misinterpreted the question, and I should have provided the reason the standard specifies it like that. One reason I can think of, is that true and false are supposed to be usable in #if preprocessing directives (as the quote from the standard mentions).

The reason ((bool) 0) or ((bool) 1) won't work in #if preprocessing directives, is because the standard doesn't allow it. In section 6.10.1 it says :

The expression that controls conditional inclusion shall be an integer constant expression except that: it shall not contain a cast;

Sander De Dycker
  • 16,053
  • 1
  • 35
  • 40
  • Good answer, but providing an actual citation would make it a great answer. – Nemo Jul 08 '11 at 16:12
  • @Nemo : the code block contains a direct quote from the C99 standard, but I could have indicated it explicitly, and done it as a quote (fixed now). – Sander De Dycker Jul 08 '11 at 16:20
  • @Rob : I'm sorry - you'll have to explain what you mean by that. Is the answer not good because it's too obvious ? – Sander De Dycker Jul 08 '11 at 16:21
  • 1
    Yes, that standard specification of course is the reason, why standard-conform implementations define the constants that way, but this does not answers the question, if there is good reason for the standard to have them expand to an integer and not to a bool. – Kaiserludi Jul 08 '11 at 16:24
  • 1
    @Sander: he means that your answer doesn't add anything to the discussion. The OP knows they are defined as ints, he is asking why the decision was made. – houbysoft Jul 08 '11 at 16:24
  • 2
    @Kaiserludi : understood. It seems I misinterpreted the question. I see now what @Rob meant too. I guess the reason for not making them of type `_Bool`, is because they are supposed to be usable in `#if` preprocessing directives, as the quote above mentions. – Sander De Dycker Jul 08 '11 at 16:28
  • @houbysoft @Rob: I believe "suitable for use in #if preprocessing directives" is one rationale. – Nemo Jul 08 '11 at 16:29
  • Just tested: This will indeed produce errors in GCC: ` #ifdef false #undef false #endif #ifdef true #undef true #endif #define false ((bool)0) #define true ((bool)1) #if false #endif ` while this will build fine: ` /*#ifdef false #undef false #endif #ifdef true #undef true #endif #define false ((bool)0) #define true ((bool)1)*/ #if false #endif ` So the preprocessor directive compatibility really seems to be the reason. – Kaiserludi Jul 08 '11 at 16:48
  • @Kaiserludi : that's because the standard says that "The expression that controls conditional inclusion shall be an integer constant expression except that: it shall not contain a cast;". My apologies again for the initial confusion on my part. – Sander De Dycker Jul 08 '11 at 17:23
  • `(bool)+1` and `(bool)+0` would be valid in `#if` though, wouldn't they? `bool` would be replaced with `_Bool` which would then be replaced with `0`. – R.. GitHub STOP HELPING ICE Oct 01 '11 at 04:48
  • @R. : interesting approach. But would it be a good idea if the standard defined `true` and `false` that way ? – Sander De Dycker Oct 01 '11 at 07:57
  • Since the only place it could matter is `sizeof true` and `sizeof false`, I think it would just be silly... – R.. GitHub STOP HELPING ICE Oct 01 '11 at 12:47
5

Beyond the other reasons already mentioned, because a _Bool becomes an int anyway as soon as you do almost anything with it.

For example, what is the type of (_Bool)0 & (_Bool)1? You might think that the expression has type _Bool, but actually §6.5.10 defines the semantics of &:

... The usual arithmetic conversions are performed on the operands ...

"usual arithmetic conversions" has a very specific meaning in the C standard. It is defined in §6.3.1.8, and includes the following:

... the integer promotions are performed on both operands ...

"integer promotions" is also a defined term, from §6.3.1.1:

If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions.48) All other types are unchanged by the integer promotions.

Although narrower types than int exist in the C standard, they are automatically widened to int in nearly any expression. Together with the fact that the result of the boolean operations has type int, this makes int a natural choice for the type of these literals.

Stephen Canon
  • 103,815
  • 19
  • 183
  • 269
  • why would you use a binary AND on two booleans? – dascandy Jul 08 '11 at 17:51
  • 1
    @dascandy: What if the two operands were function calls that returned `_Bool`, and what if the function calls had side-effects that you didn't want to lose from short-circuiting? – jamesdlin Jul 09 '11 at 00:41
  • Didn't think of that. Thanks! Good reason. I do think it's a bit terse but it would be a good use. – dascandy Jul 11 '11 at 06:20
  • 4
    @jamesdlin: If you care about the side effects of the functions, you very likely care about the **order** the two functions are called in. Actually I once tried to do something like what you suggested, and it bit me bad when a particular compiler decided to reverse the order of the calls... I would now go so far as to say it's almost always harmful to use multiple functions with side effects as operands to arithmetic/bitwise operators. – R.. GitHub STOP HELPING ICE Oct 01 '11 at 04:49
3

All the other answers are trying to use the standard to justify why the standard defines things a certain way, which I find unsatisfactory. The standard defines not only the types, but also the operators and the preprocessor, so if C99 was introducing a Boolean type, why not change all the Boolean operators to evaluate to a value of that type and extend the preprocessor to support Boolean types?

To do so would be possible, but more complicated the necessary. It was far easier for the standard-writers and the compiler-writers to make only the minimum necessary changes to add a new Boolean type to the language. Since all the Boolean operations still evaluate to type int, all the pre-C99 compilers can be updated to support C99 without having to change their type-evaluation code for all the basic operators, and the standard-writers can be more confident that the new Boolean feature hasn't accidentally introduced inconsistencies to parts of the standard that had previously been fine. All they needed to do was make sure the "usual arithmetic conversions" applied to _Bool, and then everything else is guaranteed to work.

It's not a technical reason. It's a practical one.

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
2

Firstly, although _Bool may not be int, it is required that a _Bool can accept the values 0 and 1, therefore expanding true and false to 1 and 0 are fine.

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

Also, for backward compatibility, true and false are reasonable to be ints, because all logical operators return int.

C99 §6.5.3.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. The result has type int. The expression !E is equivalent to (0==E).

C99 §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.90) The result has type int.

C99 §6.5.9/3: The == (equal to) and != (not equal to) operators are analogous to the relational operators except for their lower precedence.91) 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.

C99 §6.5.13/3: The && operator shall yield 1 if both of its operands compare unequal to 0; otherwise, it yields 0. The result has type int.

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

And finally, as @Sander De Dycker mentioned, the standard defined true and false be expanded like that (C99 §7.16/3).

Community
  • 1
  • 1
kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • But you normally will check logical operators without using true or false and write code like "if(foo)" and "if(!foo)" (because implementation-specif TRUE-macro it is only guaranteed to be !=FALSE, but not to be 1, and if you write code like "if(foo==true)", then you could accidentally acquire that habit for TRUE). Also if avoiding implicit casts when comparing logical operator results to true/false would be an issue, then saving these results into bools or checking a bool against true/false would have the same problem and it would be best for the standard to define bool as int. – Kaiserludi Jul 08 '11 at 16:39