Why does "sizeof(a ? true : false)" give an output of four bytes?
I have a small piece of code about the sizeof
operator with the ternary operator:
#include <stdio.h>
#include <stdbool.h>
int main()
{
bool a = true;
printf("%zu\n", sizeof(bool)); // Ok
printf("%zu\n", sizeof(a)); // Ok
printf("%zu\n", sizeof(a ? true : false)); // Why 4?
return 0;
}
Output (GCC):
1
1
4 // Why 4?
But here,
printf("%zu\n", sizeof(a ? true : false)); // Why 4?
the ternary operator returns boolean
type and sizeof bool
type is 1
byte in C.
Then why does sizeof(a ? true : false)
give an output of four bytes?
It's because you have #include <stdbool.h>
. That header defines macros true
and false
to be 1
and 0
, so your statement looks like this:
printf("%zu\n", sizeof(a ? 1 : 0)); // Why 4?
sizeof(int)
is 4 on your platform.
Here, ternary operator return
boolean
type,
OK, there's more to that!
In C, the result of this ternary operation is of type int
. [notes below (1,2)]
Hence the result is the same as the expression sizeof(int)
, on your platform.
Note 1: Quoting C11
, chapter §7.18, Boolean type and values <stdbool.h>
[....] 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, [....]
Note 2: For conditional operator, chapter §6.5.15, (emphasis mine)
The first operand is evaluated; there is a sequence point between its evaluation and the evaluation of the second or third operand (whichever is evaluated). The second operand is evaluated only if the first compares unequal to 0; the third operand is evaluated only if the first compares equal to 0; the result is the value of the second or third operand (whichever is evaluated), [...]
and
If both the second and third operands have arithmetic type, the result type that would be determined by the usual arithmetic conversions, were they applied to those two operands, is the type of the result. [....]
hence, the result will be of type integer and because of the value range, the constants are precisely of type int
.
That said, a generic advice, int main()
should better be int main (void)
to be truly standard-conforming.
The ternary operator is a red herring.
printf("%zu\n", sizeof(true));
prints 4 (or whatever sizeof(int)
is on your platform).
The following assumes that bool
is a synonym for char
or a similar type of size 1, and int
is larger than char
.
The reason why sizeof(true) != sizeof(bool)
and sizeof(true) == sizeof(int)
is simply because true
is not an expression of type bool
. It's an expression of type int
. It is #define
d as 1
in stdbool.h
.
There are no rvalues of type Edit: this paragraph is not true, arguments to bool
in C at all. Every such rvalue is immediately promoted to int
, even when used as an argument to sizeof
.sizeof
don't get promoted to int
. This doesn't affect any of the conclusions though.
Regarding the boolean type in C
A boolean type was introduced fairly late in the C language, in the year 1999. Before then, C did not have a boolean type but instead used int
for all boolean expressions. Therefore all logical operators such as > == !
etc return an int
of value 1
or 0
.
It was custom for applications to use home-made types such as typedef enum { FALSE, TRUE } BOOL;
, which also boils down to int
-sized types.
C++ had a much better, and explicit boolean type, bool
, which was no larger than 1 byte. While the boolean types or expressions in C would end up as 4 bytes in the worst case. Some manner of compatibility with C++ was introduced in C with the C99 standard. C then got a boolean type _Bool
and also the header stdbool.h
.
stdbool.h
provides some compatibility with C++. This header defines the macro bool
(same spelling as C++ keyword) that expands to _Bool
, a type which is a small integer type, likely 1 byte large. Similarly, the header provides two macros true
and false
, same spelling as C++ keywords, but with backward compatibility to older C programs. Therefore true
and false
expand to 1
and 0
in C and their type is int
. These macros are not actually of the boolean type like the corresponding C++ keywords would be.
Similarly, for backward compatibility purposes, logical operators in C still return an int
to this day, even though C nowadays got a boolean type. While in C++, logical operators return a bool
. Thus an expression such as sizeof(a == b)
will give the size of an int
in C, but the size of a bool
in C++.
Regarding the conditional operator ?:
The conditional operator ?:
is a weird operator with a couple of quirks. It is a common mistake to believe that it is 100% equivalent to if() { } else {}
. Not quite.
There is a sequence point between the evaluation of the 1st and the 2nd or 3rd operand. The ?:
operator is guaranteed to only evaluate either the 2nd or the 3rd operand, so it can't execute any side-effects of the operand that is not evaluated. Code like true? func1() : func2()
will not execute func2()
. So far, so good.
However, there is a special rule stating that the 2nd and 3rd operand must get implicitly type promoted and balanced against each other with the usual arithmetic conversions. (Implicit type promotion rules in C explained here). This means that the 2nd or 3rd operand will always be at least as large as an int
.
So it doesn't matter that true
and false
happen to be of type int
in C because the expression would always give at least the size of an int
no matter.
Even if you would rewrite the expression to sizeof(a ? (bool)true : (bool)false)
it would still return the size of an int
!
This is because of implicit type promotion through the usual arithmetic conversions.
Quick answer:
-
sizeof(a ? true : false)
evaluates to4
becausetrue
andfalse
are defined in<stdbool.h>
as1
and0
respectively, so the expression expands tosizeof(a ? 1 : 0)
which is an integer expression with typeint
, that occupies 4 bytes on your platform. For the same reason,sizeof(true)
would also evaluate to4
on your system.
Note however that:
sizeof(a ? a : a)
also evaluates to4
because the ternary operator performs the integer promotions on its second and third operands if these are integer expressions. The same of course happens forsizeof(a ? true : false)
andsizeof(a ? (bool)true : (bool)false)
, but casting the whole expression asbool
behaves as expected:sizeof((bool)(a ? true : false)) -> 1
.also note that comparison operators evaluate to boolean values
1
or0
, but haveint
type:sizeof(a == a) -> 4
.
The only operators that keep the boolean nature of a
would be:
the comma operator: both
sizeof(a, a)
andsizeof(true, a)
evaluate to1
at compile time.the assignment operators: both
sizeof(a = a)
andsizeof(a = true)
have a value of1
.the increment operators:
sizeof(a++) -> 1
Finally, all of the above applies to C only: C++ has different semantics regarding the bool
type, boolean values true
and false
, comparison operators and the ternary operator: all of these sizeof()
expressions evaluate to 1
in C++.