1

I cannot help myself but struggle to understand why is the ternary conditional's 3rd operand (the false-condition expression) subject to implicit type conversion even when the control condition evaluates to true and vice-versa?

I can probably only reason with the fact that the C language standard specification requires that the operands are "compatible" (I've heard this terminology everywhere, but I am not sure how accurate it is) and just like in any expression, the compiler makes sure that they are. But why is this restriction on the first place? The ternary is not like other operators in that it can freely disregard the expression that will not be evaluated. For other operations it is evident that the operands ought to be of the same type for the particular operation to work as intended.

That would also only make sense if the Implicit Type Conversion occur at compile time, and to be honest, I don't even really know when it takes place. For some reason I cannot find a sane answer to this question, but I did found this article. This article doesn't make any sense to me, particularly this line:

Implicit type conversion in C language is the conversion of one data type into another datatype by the compiler during the execution of the program.

Is it by the compiler, or is it during the execution of the program? I may be an easy one to get confused, but as far as I am aware the compiler has nothing to do during the program's execution.

So, can anyone please explain why is the ITC conversion all that necessary, because to me this yields an unnecessary overhead. Here is a proof-of-concept:

#include <stdio.h>

int main (void)
{
    printf("%zu\n", sizeof( 0 ? 2.0 : 3 ));
   
    return 0;
}

The result is 8.

  • Also I asked ChatGPT when does ITC occur and it wrote at run time. I asked it if it is sure and to validate that information and not surprisingly it changed its narrative.
Edenia
  • 2,312
  • 1
  • 16
  • 33
  • `sizeof` happens at compile time, so the type of the inner expression must also be known at compile time. – Retired Ninja May 19 '23 at 03:09
  • 1
    @RetiredNinja What about `int sz=12; printf("%zu\n", sizeof(char[(0 ? 2.0 : sz)]));` ? – Edenia May 19 '23 at 03:15
  • @RetiredNinja In that case the sizeof operand is evaluated at run time. – Edenia May 19 '23 at 03:17
  • Read the standard — [§.5.15 Conditional operator](http://port70.net/~nsz/c/c11/n1570.html#6.5.15). It says: _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), converted to the type described below. —— 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._ – Jonathan Leffler May 19 '23 at 03:23
  • Since you're mixing a `double` and an `int` value, the result is of type `double` because that's what the "[usual arithmetic conversions](http://port70.net/~nsz/c/c11/n1570.html#6.3.1.8)" would give. In your example of an array, you should be getting a compilation error; the size of an array must be an integer type, and your code specifies the size as a `double`. – Jonathan Leffler May 19 '23 at 03:26
  • @JonathanLeffler So the usual arithmetic conversions occur at compile time? – Edenia May 19 '23 at 03:34
  • @Edenia no, but at compile-time the compiler can predict what the type of the result of the conversion will be (since it only depends on the types and not the values) – M.M May 19 '23 at 03:37
  • The compiler takes into account the usual arithmetic conversions at compile time. If the expression can be evaluated at compile time, the compiler will do so. Otherwise, it generates code to ensure the correct conversions and evaluations occur at run time. Except when evaluating the size of a variable-length array, the result of `sizeof` can be evaluated at compile time. – Jonathan Leffler May 19 '23 at 13:15

2 Answers2

1

It's important to understand the C evaluation model: the language is defined on an abstract machine in which there is no distinction between "compile-time" and "run-time". There is a partial ordering on observable side-effects .

Converting 3 to 3.0 occurs in the abstract machine if and when the third operand of the conditional operator is evaluated.

The compiler is responsible for producing the same output that the abstract machine would produce. Because this conversion has no side-effect, the compiler could prepare it in advance. For example the compiler might prepare the converted result during compilation to avoid any runtime assembly instruction being needed.

Your sizeof example is not really relevant to this question, as the result of sizeof depends only on the type of the two arguments ; it doesn't depend on the value of the arguments or on which argument might be selected if the conditional were evaluated.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • The more I think about it, the more I understand how confused and silly I was. I thought that one of the true/false operands to the ternary can be completely ignored, but since which one cannot be determined at least at compile time, they both have to be of the same type and that obviously does not happen at run time. I at least had a very good reason for being confused though. – Edenia May 19 '23 at 03:48
1

C is a statically typed language, except for variably modified types. (A variably modified type is a type involving a variable length array.) Because of this, each expression must have a type. So where a conditional operator appears, the expression that it forms must have a type.

In most cases, the type of the conditional operator is determined by rules in C 2018 6.5.15 5 and 6, including using the usual arithmetic conversion for arithmetic operands. These rules result in a static type determined at compile type except for pointers involving variable length arrays. (Variable length arrays cannot be operands of the conditional operand, so variably modified types are present only as pointers.)

Therefore, when a conditional operator expression is evaluated, whichever of the second and third operands is evaluated must be converted to the result type. That is why there is an implicit conversion in the operation.

This evaluation is performed during program execution unless the C implementation is able to determine the condition during translation and optimizes the expression. In any case, the unevaluated operand is not converted.

(Note there is a defect in the C standard here. If the second or third operand is a variably modified type and the condition selects the other operand for evaluation, the C implementation cannot know what type the result should be without evaluating the size aspect of the unselected operand.)

I can probably only reason with the fact that the C language standard specification requires that the operands are "compatible"

The C standard does not require the operands be compatible. “Compatible” is a specific term in the C standard that means two types can be completed to be the same type. For example, int [] and int [3] are compatible because an array of unknown size can be completed to be an array of 3 elements.

A conditional expression can be x ? 3. : -1 even though double and int are not compatible types.

The constraints for the second and third operands of the conditional operator are in C 2018 6.5.15 3. If the operands are arithmetic, those constraints do not require them to be compatible.

That would also only make sense if the Implicit Type Conversion occur at compile time…

I see no reason for this conclusion. In double foo(int x) { return x; }, the int x is implicitly converted to double, and this conversion occurs at run time (except when optimization permits it to be done at compile time). Why would the implicit conversion in the conditional operator be any different?

Implicit type conversion in C language is the conversion of one data type into another datatype by the compiler during the execution of the program.

Is it by the compiler, or is it during the execution of the program?

The article you cite is poorly written. It means the conversion occurs during program execution but is performed by instructions written by the compiler.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • This completely filled the gaps and cleared out any confusion. The concept of the ternary caused some premature confusion at first (and because I didn't exactly knew when does the type conversion occur, and in the very least I would expected it to be at compile time). Also things are a little bit different when you are working in an interpreted environment. – Edenia May 19 '23 at 16:51