Does this equivalency return a bool or int?

Viewed 68

I would like to write the following function to return whether a queue is empty or not:

#include <stdbool.h>
bool QueueIsEmpty(Queue *pq)
{
    return (pq->items == 0)? true : false;
}

Would doing the following shorthand work for this?

bool QueueIsEmpty(Queue *pq)
{
    return pq->items == 0;
}

In other words, does that return an int or a bool when doing the equivalency check? And if it returns a bool how does the compiler know to cast it to a bool, or is a bool just an int behind-the-scenes?

When trying it in Compiler Explorer, both seem to just do mov eax, 0 and produce identical assembly: https://godbolt.org/z/bY59r3.

4 Answers

Both are equivalent.

bool is considered an integer type which can only hold the values 0 and 1. true has a value of 1 while false has a value of 0.

The expression pq->items == 0 has type int and will evaluate to either 0 or 1. These are both valid values for bool so the value will be properly converted.

The value of comparison expression using any of ==, !=, <, >, <=, => will be a value 1 or 0 for true and false respectively, and, due to history, will be of type int.

_Bool type (for which bool is a macro alias defined in <stdbool.h>) can only store values 1 and 0. Additionally, when converting to _Bool, any nonzero value will be converted to 1. C11 6.3.1.2:

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

[59] NaNs do not compare equal to 0 and thus convert to 1.

Therefore the following is valid:

bool QueueIsEmpty(Queue *pq)
{
    return pq->items == 0;
}

The !x unary operator is exactly equivalent to x == 0 semantically and syntactically in all contexts, and therefore the former can be substituted everywhere in place of the latter, therefore the following is valid as well:

bool QueueIsEmpty(Queue *pq)
{
    return !pq->items;
}

and the following is also valid for a function with opposite semantics:

bool QueueHasItems(Queue *pq)
{
    return pq->items;
}

All of the functions have the same semantics regardless of the type of pq->items provided that the value of this expression has a scalar type (i.e. boolean, integer, pointer, real or complex floating point), or decays to one (i.e. if items is an array, then it will always be true...); and if it is not a scalar type then the code should fail to compile...

This compiles on x86-64 to

QueueHasItems:
        cmp     QWORD PTR [rdi], 0
        setne   al
        ret

i.e. al is set as the result of zero/equality flag after cmp pq->items, 0.

pq->items == 0 results in an int with the value of 0 or 1.

bool QueueIsEmpty() returns a bool with the value of 0 or 1. With stdbool.h, a bool can only have the value of 0 or 1.

In C, there is no true bool type1. C uses the concept of falsey and truthy; if the expression evaluates to something non-zero, it can be used as truthy, otherwise, it is falsey.

Therefore in both cases you have, they will have the same effect.

You could also use the ! operator and do !pq->items;, which according to the logical operators documentation is the same as doing pq->items == 0

1 Since C99, C now supports boolean arithmetic using the _Bool type, and if you include stdbool.h, you can even get access to the macros true or false, which expand to 1 or 0, respectively.

Related