4

From some reading on Stack Overflow, I gather that bool, as defined in stdbool.h, is a macro that expands to the built-in type _Bool, and that true is defined as 1 and false is defined as 0.

Is casting to bool guaranteed to return a value of 0 or 1? Might a _Bool variable, including cast rvalues, ever attain a value other than 0 or 1?

The following code implies that _Bool variables, including cast rvalues, will only have values of 0 or 1, but I'd like to confirm that this is guaranteed, and that I'm not observing target- or compiler version-specific behavior.

(FWIW, I'm most specifically interested in confirming that only NULL, cast to _Bool, will be 0, and that all other possible pointer values, cast to _Bool, will be 1)

$ cat ./main.c
#include <stdio.h>
#include <stdbool.h>

int main( int argc, char* argv )
{
  int   i;
  int*  p = &i;
  int*  n = NULL;

  printf( "%zu\n", sizeof( (bool)p ) );
  printf( "%p - %d\n", p, (bool)p );
  printf( "%p - %d\n", n, (bool)n );
  printf( "%d\n", (bool)3 );

  return 0;
}
$ gcc --version
gcc (GCC) 8.2.1 20181215 (Red Hat 8.2.1-6)
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ gcc -g ./main.c && ./a.out    
1
0x7ffdac3290bc - 1
(nil) - 0
1
StoneThrow
  • 5,314
  • 4
  • 44
  • 86
  • @AlexLop. - It did, thank you. I thought I did my due diligence in looking for relevant existing posts, but I guess an answer was out there. Still grateful for the existing answers. – StoneThrow Nov 16 '19 at 18:22

2 Answers2

3

C 2018 6.3.1.2 1 says:

When any scalar value is converted to _Bool, the result is 0 if the value compares equal to 0; otherwise, the result is 1.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • I think I realized an interesting corner case: I remember learning long ago that although `NULL` is all-but-certainly `0`, it is not guaranteed. In that case, `(bool)NULL` would evaluate to `1` and only the non-`NULL` pointer `0x0` would evaluate to `0` when cast to `bool`. – StoneThrow Nov 16 '19 at 02:02
  • 1
    @StoneThrow: If a pointer is a null pointer, including `NULL`, it compares equal to 0, regardless of how the C implementation represents a null pointer. – Eric Postpischil Nov 16 '19 at 02:22
  • @StoneThrow: This is wrong. Whether the result of conversion to `bool` is 0 or 1 has nothing to do with how null pointers are represented. It's 0 if the pointer is null and 1 if the pointer is not null. – R.. GitHub STOP HELPING ICE Nov 16 '19 at 02:22
  • @StoneThrow "`(bool)NULL` would evaluate to 1" is possible (when `NULL` is some non-zero integer). `(bool)non_null_pointer` always evaluates to value of 1 - regardless how `NULL` is defined. – chux - Reinstate Monica Nov 16 '19 at 04:18
  • @chux-ReinstateMonica: `(bool) NULL` is always 0, unless `bool` or `NULL` is defined other than as provided in standard headers. `NULL` is either 0 or 0 converted to `void *`. Comparing 0 to 0 evaluates to true. Comparing 0 converted to `void *` to 0 evaluates to true, regardless of how it is represented. Therefore, by 6.3.1.2 1, `(bool) NULL` evaluates to 0. – Eric Postpischil Nov 16 '19 at 11:42
  • Eric, agree with "An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant". Are you suggesting that no other integer can also be another _null pointer constant_ - an implementation can have only 1? That is the crux. As I read C spec, a system can has more than one like 0 _and_ -1, and `NULL` can be either one of them, even if that is uncommon. I am sure we agree there can be more than one _null pointer_. Yet this issue seems to be about _null pointer constant_ uniqueness. For if unique, I concur: `(bool) NULL` is --> 0` – chux - Reinstate Monica Nov 16 '19 at 16:19
  • @chux-ReinstateMonica: First, per C 2018 6.3.2.3 3, a null pointer constant is an integer constant expression with value 0 or such an expression cast to `void *`. If an integer constant is not 0, it is not a null pointer constant. Second, by 6.5.9 5 and 6, any null pointer compares equal to 0. It does not matter how the null pointer is derived or represented: Comparing a null pointer and 0 results in 0 being converted to the type of the pointer (paragraph 5), and then there are two null pointers, and they compare equal (paragraph 6). – Eric Postpischil Nov 16 '19 at 17:43
  • We have no disagreement on _null pointer_, just _null pointer constant_. Given A --> B "integer constant expression with the value 0, ... is called a null pointer constant" does not imply /A --> /B "If an integer constant is not 0, it is not a null pointer constant." . Perhaps a deeper question for another day? – chux - Reinstate Monica Nov 16 '19 at 17:53
  • @chux: The first sentence in 6.3.2.3 is not a expressing a logical implication. It defines a term. Furthermore, it is irrelevant: per 6.5.9 5 and 6, a null pointer compares equal to 0 regardless of the derivation or representation of the null pointer. – Eric Postpischil Nov 16 '19 at 18:07
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/202475/discussion-between-chux-reinstate-monica-and-eric-postpischil). – chux - Reinstate Monica Nov 16 '19 at 18:09
2

Yes, a cast to bool is guaranteed to result in 0 or 1, up to the limit that undefined behavior can produce anything. If there's undefined behavior leading up to the path where conversion to bool happens, it may so happen that you observe strange things, since the compiler may have rightfully made transformations based on assumptions that are no longer valid. For example, if the implementation performs the collapse to 0/1 at store time, it may assume when loading a bool from memory that the value is already 0 or 1, and does not need further collapse. If the storage for the bool object has been altered via a buffer overflow, aliasing violation, etc. then a different value might end up being seen.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • 1
    That is quite a broad disclaimer. It would apply to anything. `*` multiplies, unless there is undefined behavior leading up to it. `printf` prints, unless there is undefined behavior leading up to it.… – Eric Postpischil Nov 16 '19 at 02:02
  • 1
    @EricPostpischil: Indeed, but I feel like it needs saying here because a lot of users of the language don't really grasp how undefined UB is, and reading a value other than 0 or 1 from an object that "can't have other values" is really surprising if you don't have an intuition for UB yet. – R.. GitHub STOP HELPING ICE Nov 16 '19 at 02:21