Jul 15 2015 • Ender Dai

Ternary operator madness

Life is full of surprise. Things get even messier with implicit type conversion. Let’s talk about the ternary operator (or conditional operator) ?: in C.

What is the value of 1?-1:1? It is obvious, isn’t it? How about 1?-1:1U? “The same”, I hear your voice. So (1?-1:1)==(1?-1:1U) must be true, as well as (1:-1:1)<0 && (1:-1:1U)<0. If you believe that is the truth here, try it out and you will be surprised. (No I will not paste the answer here, try it out by yourself so you can remember:)

So the problem here is obviously caused by U, which is the only difference between those two expressions. -1 and 1 are int, while 1U is unsigned int. When we mix them together in an expression, int will be promoted to unsigned int. So actually we get -1 for 1?-1:1 but (unsigned int)-1 for 1?-1:1U. Now everything is clear: this is all about type promotion. Nigel Jones wrote an essay1 on this topic in which he provided another interesting example:

Why do we need the type promotion in this case? Can we just return -1 or 1U for cond?-1:1U? One way to think about this is that C is statically typed. The type of cond?-1:1U is determined in compile time. It can not be “either int or unsigned int”. We need to cast/promote one type to another and from int to unsigned int is the nature choice. It is an error if the type casting can not be performed in a meaningful way. For example, if a is int and b is struct, then compiler will give us an error for cond?a:b.

At last, as a bonus point, ?: in C is slight different from in C++2. ?: can return a lvalue in C++ but not in C. That is to say, the snippet below is valid only for C++.

However, changing a to be unsigned int makes the code invalid even for C++:

Here b will be cast to unsigned int. An intermediate variable must be created to store the casting result. Why? Consider the case when we cast from char to int, the original variable can not be reused because it is not long enough to hold an int. As there is no way for programmer to reference the intermediate variable, it is meaningless to allow it can be used as lvalue. In general, (int)a=1; is invalid for both C and C++.

Reference