The ternary (conditional) operator in C

2019-01-03 06:04发布

What is the need for the conditional operator? Functionally it is redundant, since it implements an if-else construct. If the conditional operator is more efficient than the equivalent if-else assignment, why can't if-else be interpreted more efficiently by the compiler?

15条回答
Rolldiameter
2楼-- · 2019-01-03 06:56

Compactness and the ability to inline an if-then-else construct into an expression.

查看更多
贪生不怕死
3楼-- · 2019-01-03 06:56

The fact that the ternary operator is an expression, not a statement, allows it to be used in macro expansions for function-like macros that are used as part of an expression. Const may not have been part of original C, but the macro pre-processor goes way back.

One place where I've seen it used is in an array package that used macros for bound-checked array accesses. The syntax for a checked reference was something like aref(arrayname, type, index), where arrayname was actually a pointer to a struct that included the array bounds and an unsigned char array for the data, type was the actual type of the data, and index was the index. The expansion of this was quite hairy (and I'm not going to do it from memory), but it used some ternary operators to do the bound checking.

You can't do this as a function call in C because of the need for polymorphism of the returned object. So a macro was needed to do the type casting in the expression. In C++ you could do this as a templated overloaded function call (probably for operator[]), but C doesn't have such features.

Edit: Here's the example I was talking about, from the Berkeley CAD array package (glu 1.4 edition). The documentation of the array_fetch usage is:

type
array_fetch(type, array, position)
typeof type;
array_t *array;
int position;

Fetch an element from an array. A runtime error occurs on an attempt to reference outside the bounds of the array. There is no type-checking that the value at the given position is actually of the type used when dereferencing the array.

and here is the macro defintion of array_fetch (note the use of the ternary operator and the comma sequencing operator to execute all the subexpressions with the right values in the right order as part of a single expression):

#define array_fetch(type, a, i)         \
(array_global_index = (i),              \
  (array_global_index >= (a)->num) ? array_abort((a),1) : 0,\
  *((type *) ((a)->space + array_global_index * (a)->obj_size)))

The expansion for array_insert ( which grows the array if necessary, like a C++ vector) is even hairier, involving multiple nested ternary operators.

查看更多
够拽才男人
4楼-- · 2019-01-03 07:01
  • Some of the more obscure operators in C exist solely because they allow implementation of various function-like macros as a single expression that returns a result. I would say that this is the main purpose why the ?: and , operators are allowed to exist, even though their functionality is otherwise redundant.

    Lets say we wish to implement a function-like macro that returns the largest of two parameters. It would then be called as for example:

    int x = LARGEST(1,2);
    

    The only way to implement this as a function-like macro would be

    #define LARGEST(x,y) ((x) > (y) ? (x) : (y))
    

    It wouldn't be possible with an if ... else statement, since it does not return a result value. Note)

  • The other purpose of ?: is that it in some cases actually increases readability. Most often if...else is more readable, but not always. Take for example long, repetitive switch statements:

    switch(something)
    {
      case A: 
        if(x == A)
        {
          array[i] = x;
        }
        else
        {
          array[i] = y;
        }
        break;
    
      case B: 
        if(x == B)
        {
          array[i] = x;
        }
        else
        {
          array[i] = y;
        }
        break;
      ...
    }
    

    This can be replaced with the far more readable

    switch(something)
    {
      case A: array[i] = (x == A) ? x : y; break;
      case B: array[i] = (x == B) ? x : y; break;
      ...
    }
    
  • Please note that ?: does never result in faster code than if-else. That's some strange myth created by confused beginners. In case of optimized code, ?: gives identical performance as if-else in the vast majority of the cases.

    If anything, ?: can be slower than if-else, because it comes with mandatory implicit type promotions, even of the operand which is not going to be used. But ?: can never be faster than if-else.


Note) Now of course someone will argue and wonder why not use a function. Indeed if you can use a function, it is always preferable over a function-like macro. But sometimes you can't use functions. Suppose for example that x in the example above is declared at file scope. The initializer must then be a constant expression, so it cannot contain a function call. Other practical examples of where you have to use function-like macros involve type safe programming with _Generic or "X macros".

查看更多
登录 后发表回答