136

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?

msc
  • 30,333
  • 19
  • 96
  • 184
  • 40
    `sizeof(true)` and `sizeof(false)` is also 4: http://ide.geeksforgeeks.org/O5jvuN – tkausl Oct 30 '17 at 08:40
  • 2
    if it is `C`, then its correct because `true` and `false` are integer constants – kmdreko Oct 30 '17 at 08:41
  • 2
    @vu1p3n0x note the wording "integer constant" doesn't require a specific integer type. `true` and `false` could be defined as `((char)1)` and `((char)0)`. –  Oct 30 '17 at 09:01
  • 7
    The more interesting question here would be why this implementation is "inconsistent" in that it obviously defines `_Bool` to have size 1, but not `true` and `false`. But the standard doesn't have anything to say about that as far as I can tell. –  Oct 30 '17 at 09:02
  • 12
    @FelixPalmen same reason why given `char a;` `sizeof(a) == 1` and `sizeof('a') == sizeof(int)` (in C). It's not about the implementation, it's about the language. – n. 'pronouns' m. Oct 30 '17 at 09:12
  • @n.m. `'a'` is a constant of type `int`, while `true` is a macro defined to the integer constant 1. I see a difference here, but it's certainly about how you interpret that wording. –  Oct 30 '17 at 09:14
  • @FelixPalmen What is this difference you are speaking of? I can't see any. After preprocessing, `1` is a constant of type `int`, just like `'a'`. – n. 'pronouns' m. Oct 30 '17 at 09:16
  • 10
    Have you tried to print `sizeof(true)`? perhaps it will make thins a bit more clear (in particular, it will become obvious that the ternary operator is a red herring). – n. 'pronouns' m. Oct 30 '17 at 09:17
  • 1
    @n.m. I'm referring to the ambiguity of the term "integer constant", that doesn't force the type to be `int`. Of course, if you read the `1` as the *literal* definition of `true` (I'm not sure it can be interpreted that way), *then* the type is `int`.... –  Oct 30 '17 at 09:19
  • 4
    @FelixPalmen `true` is `#define`d to be 1 by `stdbool.h` so yes, this is the literal definition. – n. 'pronouns' m. Oct 30 '17 at 09:29
  • 3
    @FelixPalmen What ambiguity? Integer constants are defined in section 6.4.4.1 (whose name is "Integer constants"), and their types is specified in §5 of this section. It's indeed at least (`unsigned`) `int`. – Virgile Oct 30 '17 at 09:32
  • 1
    @Virgile well, as a "lexical element", yes. I agree that's probably meant, and it doesn't even matter much because a `#define true ((_Bool)1)` wouldn't buy you anything, given implicit conversions and integer promotion. Still, even the chapter on "integer constants" has a contradiction in the "semantics" part... –  Oct 30 '17 at 09:42
  • 1
    `printf("%zu\n", sizeof(a ? a : a));` will also print out `4`, making this question a bit more interesting. – eis Nov 01 '17 at 06:23
  • [Relevant](https://stackoverflow.com/q/12887700/541686)... – user541686 Nov 01 '17 at 10:10
  • 2
    This is a perfectly good question and should not be vote-to-closed as too broad (there are currently 2 such votes) – smci Nov 02 '17 at 09:47
  • in **stdbool.h** true is 1 also an integer.... :) – ΦXocę 웃 Пepeúpa ツ Nov 27 '17 at 16:45

7 Answers7

226

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.

Justin
  • 21,374
  • 12
  • 83
  • 129
  • 21
    "It's because you have #include " No, it is not. `sizeof(a ? (uint8_t)1 : (uint8_t)0);` would also give a result of 4. The integer promotion of the `?:` operands is the important part here, not the size of `true` and `false`. – Lundin Oct 31 '17 at 09:14
  • 9
    @Lundin: Both are important. As-written, the type is already `int` with no promotion. The reason you can't "fix" it is the default promotions. – R.. GitHub STOP HELPING ICE Oct 31 '17 at 16:28
  • 5
    @PeterSchneider This is not C++. This is C. In C++, `true` and `false` are *not* macros; they are keywords. They aren't defined to be `1` and `0`, but to be the true and false values of the `bool` type. – Justin Oct 31 '17 at 18:49
  • 5
    @PeterSchneider No, you learned something about C today. Don't confuse the two languages. In C++, `sizeof(true)` is 1. [demo](https://godbolt.org/g/UG6tEq). – Rakete1111 Oct 31 '17 at 18:52
  • 1
    True, mixed it up. Hadn't read carefully and was missleaded by cppreference-link. My fault, thank you. But I have this feeling about c++ anyway. – Peter Schneider Oct 31 '17 at 18:56
67

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.

Sourav Ghosh
  • 127,934
  • 16
  • 167
  • 234
59

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 #defined as 1 in stdbool.h.

There are no rvalues of type bool in C at all. Every such rvalue is immediately promoted to int, even when used as an argument to sizeof. Edit: this paragraph is not true, arguments to sizeof don't get promoted to int. This doesn't affect any of the conclusions though.

n. 'pronouns' m.
  • 95,181
  • 13
  • 111
  • 206
  • Nice answer. After I read the currently most upvoted answer, I was thinking all statements should evaluate to 4. This cleared things up. +1 – Pedro A Oct 30 '17 at 11:58
  • 5
    Isn't `(bool)1` an rvalue of type `bool`? – Ben Voigt Oct 30 '17 at 19:53
  • `printf("%u\n", sizeof((char) 1));` prints `1` on my platform whereas `printf("%u\n", sizeof(1));` prints `4`. Doesn't this mean that your statement "Every such rvalue is immediately promoted to int, even when used as an argument to sizeof" is false? – JonatanE Oct 31 '17 at 07:48
  • This doesn't really answer the question. The size and type of `true` etc doesn't really matter in the case of `?:` since it gets integer promoted to `int` anyhow. That is, the answer should address _why_ `?:` is a red herring. – Lundin Oct 31 '17 at 09:12
  • @Lundin once you know that `sizeof(true)==sizeof(int)`, would you still wonder why `sizeof(1?true:false) == sizeof(int)`? Does it require a separate explanation? – n. 'pronouns' m. Oct 31 '17 at 09:30
  • Well... for example, go compile `sizeof(1?true:false) ` in C++ where the size of true and false is 1. Still the result 4. – Lundin Oct 31 '17 at 09:32
  • @Lundin the questiin is specifically about C, not about any language with a name that begins with a C. – n. 'pronouns' m. Oct 31 '17 at 09:36
  • So compile `sizeof(1?(char)true:(char)false)` in C then. Same issue. – Lundin Oct 31 '17 at 09:37
  • @Lundin The question doesn't ask about `sizeof(1?(char)true:(char)false)`. It asks about `sizeof(a ? true : false)`. That's what I'm answering. – n. 'pronouns' m. Oct 31 '17 at 09:38
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/157869/discussion-between-n-m-and-lundin). – n. 'pronouns' m. Oct 31 '17 at 09:42
  • 6
    I think the answer addresses the issue in the best way possible. You are welcome to downvote or improve it. – n. 'pronouns' m. Oct 31 '17 at 09:46
  • `sizeof()` usually does not evaluate it's arguments, so that means that it does not promote them. It literally takes the size in bytes of the expression in the `( )`. There is an exception though when `sizeof()` does evaluates it's arguments. https://stackoverflow.com/a/47336577/5500589 – Galaxy Nov 15 '18 at 02:19
32

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.

Farzad Karimi
  • 701
  • 10
  • 25
Lundin
  • 155,020
  • 33
  • 213
  • 341
  • 1
    C++ does not actually guarantee `sizeof(bool)==1`. – aschepler Nov 01 '17 at 00:24
  • 1
    @aschepler No but the real world outside the C++ standard does however guarantee it. Name one compiler where it isn't 1. – Lundin Nov 01 '17 at 07:30
  • Hi. I think this answer would be better _without_ its first part. The second part answers the question. The rest, while interesting, is just noise. – YSC Sep 28 '18 at 08:28
  • @YSC This was originally tagged C and C++ both, so a comparison between their different bool types and the history behind them was necessary. I doubt I would have written the first part if not for the C++ tag. However, one has to understand why sizeof(bool) is 1 but sizeof(false) is 4 in C. – Lundin Sep 28 '18 at 09:49
21

Quick answer:

  • sizeof(a ? true : false) evaluates to 4 because true and false are defined in <stdbool.h> as 1 and 0 respectively, so the expression expands to sizeof(a ? 1 : 0) which is an integer expression with type int, that occupies 4 bytes on your platform. For the same reason, sizeof(true) would also evaluate to 4 on your system.

Note however that:

  • sizeof(a ? a : a) also evaluates to 4 because the ternary operator performs the integer promotions on its second and third operands if these are integer expressions. The same of course happens for sizeof(a ? true : false) and sizeof(a ? (bool)true : (bool)false), but casting the whole expression as bool behaves as expected: sizeof((bool)(a ? true : false)) -> 1.

  • also note that comparison operators evaluate to boolean values 1 or 0, but have int type: sizeof(a == a) -> 4.

The only operators that keep the boolean nature of a would be:

  • the comma operator: both sizeof(a, a) and sizeof(true, a) evaluate to 1 at compile time.

  • the assignment operators: both sizeof(a = a) and sizeof(a = true) have a value of 1.

  • 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++.

chqrlie
  • 98,886
  • 10
  • 89
  • 149
  • 2
    Good answer that actually manages to point out that it doesn't really matter what type `true` and `false` are, because the `?:` operands would get integer promoted to `int` anyhow. Thus `sizeof(a ? (uint8_t)true : (uint8_t)false)` will also yield 4 as result. – Lundin Oct 31 '17 at 09:10
  • This answer covers the main important point, value getting promoted to `int` – Chinni Oct 31 '17 at 19:52
1

Here is a snippet from which is what included in the source

#ifndef __cplusplus

#define bool    _Bool
#define true    1
#define false   0

#else /* __cplusplus */

There macros true and false are declared as 1 and 0 respectively.

however in this case the type is the type of the literal constants. Both 0 and 1 are integer constants that fit in an int, so their type is int.

and the sizeof(int) in your case is 4.

Potato
  • 3,320
  • 2
  • 14
  • 31
-3

There is no boolean datatype in C, instead logical expressions evaluate to integer values 1 when true otherwise 0.

Conditional expressions like if, for, while, or c ? a : b expect an integer, if the number is non-zero it's considered true except for some special cases, here's a recursive sum function in which the ternary-operator will evaluate true until n reach 0.

int sum (int n) { return n ? n+sum(n-1) : n ;

It can also be used to NULL check a pointer, here's a recursive function that print the content of a Singly-Linked-List.

void print(sll * n){ printf("%d -> ",n->val); if(n->next)print(n->next); }
Khaled.K
  • 5,516
  • 1
  • 30
  • 47