简体   繁体   English

C 中的三元(条件)运算符

[英]The ternary (conditional) operator in C

What is the need for the conditional operator?条件运算符需要什么? Functionally it is redundant, since it implements an if-else construct.在功能上它是多余的,因为它实现了一个 if-else 结构。 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?如果条件运算符比等效的 if-else 赋值更有效,为什么编译器不能更有效地解释 if-else?

In C, the real utility of it is that it's an expression instead of a statement; 在C语言中,它的真正用途是它是一个表达式而不是一条语句。 that is, you can have it on the right-hand side (RHS) of a statement. 也就是说,您可以将其放在语句的右侧(RHS)。 So you can write certain things more concisely. 因此,您可以更简洁地编写某些内容。

Some of the other answers given are great. 给出的其他一些答案很好。 But I am surprised that no one mentioned that it can be used to help enforce const correctness in a compact way. 但是令我惊讶的是,没有人提到它可以用来以紧凑的方式帮助实现const正确性。

Something like this: 像这样:

const int n = (x != 0) ? 10 : 20;

so basically n is a const whose initial value is dependent on a condition statement. 因此基本上n是一个const其初始值取决于条件语句。 The easiest alternative is to make n not a const , this would allow an ordinary if to initialize it. 最简单的办法是使n不是一个const ,这将允许一个普通的if将其初始化。 But if you want it to be const , it cannot be done with an ordinary if . 但是,如果您希望将其设为const ,则无法使用常规if来完成。 The best substitute you could make would be to use a helper function like this: 您可以做的最好的替代方法是使用如下的辅助函数:

int f(int x) {
    if(x != 0) { return 10; } else { return 20; }
}

const int n = f(x);

but the ternary if version is far more compact and arguably more readable. 但是三元if版本要紧凑得多,而且可读性更高。

The ternary operator is a syntactic and readability convenience, not a performance shortcut. 三元运算符是语法和可读性的便利,而不是性能捷径。 People are split on the merits of it for conditionals of varying complexity, but for short conditions, it can be useful to have a one-line expression. 人们因其复杂性各不相同的条件而在优点上有所分歧,但对于较短的条件,使用单行表达式可能会很有用。

Moreover, since it's an expression, as Charlie Martin wrote , that means it can appear on the right-hand side of a statement in C. This is valuable for being concise. 而且,由于它是一个表达式,如查理·马丁(Charlie Martin)所写 ,这意味着它可以出现在C语言语句的右侧。这对于简洁明了。

It's crucial for code obfuscation, like this: 对于代码混淆来说至关重要,如下所示:

Look->       See?!

No
:(
Oh, well
);

紧凑性以及将if-then-else构造内联到表达式中的能力。

There are a lot of things in C that aren't technically needed because they can be more or less easily implemented in terms of other things. C中有很多东西在技术上是不需要的,因为就其他方面而言,它们或多或少可以轻松实现。 Here is an incomplete list: 这是不完整的清单:

  1. while
  2. for 对于
  3. functions 功能
  4. structs 结构

Imagine what your code would look like without these and you may find your answer. 想象一下,如果没有这些代码,您的代码将是什么样子,您可能会找到答案。 The ternary operator is a form of "syntactic sugar" that if used with care and skill makes writing and understanding code easier. 三元运算符是“语法糖”的一种形式,如果谨慎和熟练地使用它,将使编写和理解代码更加容易。

Sometimes the ternary operator is the best way to get the job done. 有时,三元运算符是完成工作的最佳方法。 In particular when you want the result of the ternary to be an l-value. 特别是当您希望三元的结果为l值时。

This is not a good example, but I'm drawing a blank on somethign better. 这不是一个很好的例子,但是我在更好的地方空白。 One thing is certian, it is not often when you really need to use the ternary, although I still use it quite a bit. 一件事是certian,虽然您仍然需要使用三进制,但是通常不需要真正使用三进制。

const char* appTitle  = amDebugging ? "DEBUG App 1.0" : "App v 1.0";

One thing I would warn against though is stringing ternaries together. 我要警告的一件事是将三元串在一起。 They become a real 他们成为真实的
problem at maintennance time: 维修时的问题:

int myVal = aIsTrue ? aVal : bIsTrue ? bVal : cIsTrue ? cVal : dVal;

EDIT : Here's a potentially better example. 编辑 :这是一个潜在的更好的例子。 You can use the ternary operator to assign references & const values where you would otherwise need to write a function to handle it: 您可以使用三元运算符来分配引用和const值,否则您将需要编写一个函数来处理它:

int getMyValue()
{
  if( myCondition )
    return 42;
  else
    return 314;
}

const int myValue = getMyValue();

...could become: ...可能成为:

const int myValue = myCondition ? 42 : 314;

Which is better is a debatable question that I will choose not to debate. 更好的是一个有争议的问题,我将选择不辩论。

Since no one has mentioned this yet, about the only way to get smart printf statements is to use the ternary operator: 由于尚无人提及,因此获取智能printf语句的唯一方法是使用三元运算符:

printf("%d item%s", count, count > 1 ? "s\n" : "\n");

Caveat: There are some differences in operator precedence when you move from C to C++ and may be surprised by the subtle bug(s) that arise thereof. 注意:从C转到C ++时,运算符优先级会有所不同,并且可能会因此而产生的细微错误感到惊讶。

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. 常量可能不是原始C的一部分,但是宏预处理器可以追溯到原来。

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. 经过检查的引用的语法类似于aref(arrayname, type, index) ,其中arrayname实际上是指向结构的指针,该结构包括数组边界和数据的无符号char数组,type是数据的实际类型,索引就是索引。 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. 由于需要返回对象的多态性,因此不能将其作为C中的函数调用来进行。 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. 在C ++中,您可以作为模板化的重载函数调用来执行此操作(可能对于operator []),但是C不具有此类功能。

Edit: Here's the example I was talking about, from the Berkeley CAD array package (glu 1.4 edition). 编辑:这是我正在谈论的示例,来自Berkeley CAD阵列软件包(glu 1.4版)。 The documentation of the array_fetch usage is: array_fetch用法的文档为:

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): 这是array_fetch的宏定义(请注意,使用三元运算符和逗号排序运算符以正确的顺序以正确的值执行所有子表达式,作为单个表达式的一部分):

#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. array_insert的扩展(在必要时可以扩展数组,例如C ++矢量)的扩展更加困难,涉及多个嵌套的三元运算符。

It's syntatic sugar and a handy shorthand for brief if/else blocks that only contain one statement. 它是语法糖,是仅包含一个语句的简短if / else块的便捷缩写。 Functionally, both constructs should perform identically. 从功能上讲,两个构造应表现相同。

三元运算符可能比常规的else子句具有更高的性能,这在嵌入式应用程序中可能很关键,但编译器优化可能会消除这种差异。

like dwn said, Performance was one of its benefits during the rise of complex processors, MSDN blog Non-classical processor behavior: How doing something can be faster than not doing it gives an example which clearly says the difference between ternary (conditional) operator and if/else statement. 就像dwn所说的那样,性能是复杂处理器兴起期间的优势之一,MSDN博客非经典处理器行为:如何比不做更快地做事给出了一个例子,清楚地表明了三元(条件)运算符与操作符之间的区别。 if / else语句。

give the following code: 给出以下代码:

#include <windows.h>
#include <stdlib.h>
#include <stdlib.h>
#include <stdio.h>

int array[10000];

int countthem(int boundary)
{
 int count = 0;
 for (int i = 0; i < 10000; i++) {
  if (array[i] < boundary) count++;
 }
 return count;
}

int __cdecl wmain(int, wchar_t **)
{
 for (int i = 0; i < 10000; i++) array[i] = rand() % 10;

 for (int boundary = 0; boundary <= 10; boundary++) {
  LARGE_INTEGER liStart, liEnd;
  QueryPerformanceCounter(&liStart);

  int count = 0;
  for (int iterations = 0; iterations < 100; iterations++) {
   count += countthem(boundary);
  }

  QueryPerformanceCounter(&liEnd);
  printf("count=%7d, time = %I64d\n",
         count, liEnd.QuadPart - liStart.QuadPart);
 }
 return 0;
}

the cost for different boundary are much different and wierd (see the original material). 不同边界的成本差异很大且很奇怪(请参阅原始材料)。 while if change: 而如果改变:

 if (array[i] < boundary) count++;

to

 count += (array[i] < boundary) ? 1 : 0;

The execution time is now independent of the boundary value, since: 现在,执行时间与边界值无关,因为:

the optimizer was able to remove the branch from the ternary expression. 优化器能够从三元表达式中删除该分支。

but on my desktop intel i5 cpu/windows 10/vs2015, my test result is quite different with msdn blog. 但是在我的台式机Intel i5 cpu / windows 10 / vs2015上,我的测试结果与msdn博客完全不同。

when using debug mode , if/else cost: 使用调试模式时 ,如果/其他成本:

count=      0, time = 6434
count= 100000, time = 7652
count= 200800, time = 10124
count= 300200, time = 12820
count= 403100, time = 15566
count= 497400, time = 16911
count= 602900, time = 15999
count= 700700, time = 12997
count= 797500, time = 11465
count= 902500, time = 7619
count=1000000, time = 6429

and ternary operator cost: 和三元运营商成本:

count=      0, time = 7045
count= 100000, time = 10194
count= 200800, time = 12080
count= 300200, time = 15007
count= 403100, time = 18519
count= 497400, time = 20957
count= 602900, time = 17851
count= 700700, time = 14593
count= 797500, time = 12390
count= 902500, time = 9283
count=1000000, time = 7020 

when using release mode , if/else cost: 使用发布模式时 ,如果/其他成本:

count=      0, time = 7
count= 100000, time = 9
count= 200800, time = 9
count= 300200, time = 9
count= 403100, time = 9
count= 497400, time = 8
count= 602900, time = 7
count= 700700, time = 7
count= 797500, time = 10
count= 902500, time = 7
count=1000000, time = 7

and ternary operator cost: 和三元运营商成本:

count=      0, time = 16
count= 100000, time = 17
count= 200800, time = 18
count= 300200, time = 16
count= 403100, time = 22
count= 497400, time = 16
count= 602900, time = 16
count= 700700, time = 15
count= 797500, time = 15
count= 902500, time = 16
count=1000000, time = 16

the ternary operator is slower than if/else statement on my machine! 三元运算符比我机器上的if / else语句慢!

so according to different compiler optimization techniques, ternal operator and if/else may behaves much different. 因此,根据不同的编译器优化技术,内部运算符和if / else的行为可能会大不相同。

  • 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. C中一些更晦涩的运算符之所以存在,仅是因为它们允许将各种类似于函数的宏实现为返回结果的单个表达式。 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. if ... else语句将无法实现,因为它不会返回结果值。 Note) 注意)

  • The other purpose of ?: is that it in some cases actually increases readability. ?:的另一个目的是在某些情况下实际上提高了可读性。 Most often if...else is more readable, but not always. 大多数情况下if...else更具可读性,但并非总是如此。 Take for example long, repetitive switch statements: 例如,使用长而重复的switch语句:

     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 . 请注意, ?: 永远不会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-else相同的性能。

    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. 如果有的话, ?:可能比if-else ,因为它带有强制的隐式类型提升,即使是将不使用的操作数也是如此。 But ?: can never be faster than if-else . 但是?:永远不会比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. 例如,假设以上示例中的x在文件范围内声明。 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". 其他必须使用类函数宏的实际示例涉及使用_Generic或“ X宏”进行类型安全的编程。

ternary = simple form of if-else. 三元= if-else的简单形式。 It is available mostly for readability. 它主要用于可读性。

The same as 与...相同

if(0)
do();


if(0)
{
do();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM