繁体   English   中英

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

[英]The ternary (conditional) operator in C

条件运算符需要什么? 在功能上它是多余的,因为它实现了一个 if-else 结构。 如果条件运算符比等效的 if-else 赋值更有效,为什么编译器不能更有效地解释 if-else?

在C语言中,它的真正用途是它是一个表达式而不是一条语句。 也就是说,您可以将其放在语句的右侧(RHS)。 因此,您可以更简洁地编写某些内容。

给出的其他一些答案很好。 但是令我惊讶的是,没有人提到它可以用来以紧凑的方式帮助实现const正确性。

像这样:

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

因此基本上n是一个const其初始值取决于条件语句。 最简单的办法是使n不是一个const ,这将允许一个普通的if将其初始化。 但是,如果您希望将其设为const ,则无法使用常规if来完成。 您可以做的最好的替代方法是使用如下的辅助函数:

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

const int n = f(x);

但是三元if版本要紧凑得多,而且可读性更高。

三元运算符是语法和可读性的便利,而不是性能捷径。 人们因其复杂性各不相同的条件而在优点上有所分歧,但对于较短的条件,使用单行表达式可能会很有用。

而且,由于它是一个表达式,如查理·马丁(Charlie Martin)所写 ,这意味着它可以出现在C语言语句的右侧。这对于简洁明了。

对于代码混淆来说至关重要,如下所示:

Look->       See?!

No
:(
Oh, well
);

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

C中有很多东西在技术上是不需要的,因为就其他方面而言,它们或多或少可以轻松实现。 这是不完整的清单:

  1. 对于
  2. 功能
  3. 结构

想象一下,如果没有这些代码,您的代码将是什么样子,您可能会找到答案。 三元运算符是“语法糖”的一种形式,如果谨慎和熟练地使用它,将使编写和理解代码更加容易。

有时,三元运算符是完成工作的最佳方法。 特别是当您希望三元的结果为l值时。

这不是一个很好的例子,但是我在更好的地方空白。 一件事是certian,虽然您仍然需要使用三进制,但是通常不需要真正使用三进制。

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

我要警告的一件事是将三元串在一起。 他们成为真实的
维修时的问题:

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

编辑 :这是一个潜在的更好的例子。 您可以使用三元运算符来分配引用和const值,否则您将需要编写一个函数来处理它:

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

const int myValue = getMyValue();

...可能成为:

const int myValue = myCondition ? 42 : 314;

更好的是一个有争议的问题,我将选择不辩论。

由于尚无人提及,因此获取智能printf语句的唯一方法是使用三元运算符:

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

注意:从C转到C ++时,运算符优先级会有所不同,并且可能会因此而产生的细微错误感到惊讶。

三元运算符是一个表达式,而不是一个语句,这一事实使得它可以在宏扩展中使用,作为函数的宏,这些宏用作表达式的一部分。 常量可能不是原始C的一部分,但是宏预处理器可以追溯到原来。

我见过它使用的一个地方是在一个数组包中,该包使用宏进行了绑定检查的数组访问。 经过检查的引用的语法类似于aref(arrayname, type, index) ,其中arrayname实际上是指向结构的指针,该结构包括数组边界和数据的无符号char数组,type是数据的实际类型,索引就是索引。 它的扩展很繁琐(我不会从内存中完成),但是它使用了一些三元运算符来进行绑定检查。

由于需要返回对象的多态性,因此不能将其作为C中的函数调用来进行。 因此,需要一个宏来在表达式中进行类型转换。 在C ++中,您可以作为模板化的重载函数调用来执行此操作(可能对于operator []),但是C不具有此类功能。

编辑:这是我正在谈论的示例,来自Berkeley CAD阵列软件包(glu 1.4版)。 array_fetch用法的文档为:

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

从数组中获取元素。 尝试在数组范围之外进行引用时发生运行时错误。 没有类型检查,指定位置的值实际上是在取消引用数组时使用的类型。

这是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)))

array_insert的扩展(在必要时可以扩展数组,例如C ++矢量)的扩展更加困难,涉及多个嵌套的三元运算符。

它是语法糖,是仅包含一个语句的简短if / else块的便捷缩写。 从功能上讲,两个构造应表现相同。

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

就像dwn所说的那样,性能是复杂处理器兴起期间的优势之一,MSDN博客非经典处理器行为:如何比不做更快地做事给出了一个例子,清楚地表明了三元(条件)运算符与操作符之间的区别。 if / else语句。

给出以下代码:

#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;
}

不同边界的成本差异很大且很奇怪(请参阅原始材料)。 而如果改变:

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

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

现在,执行时间与边界值无关,因为:

优化器能够从三元表达式中删除该分支。

但是在我的台式机Intel i5 cpu / windows 10 / vs2015上,我的测试结果与msdn博客完全不同。

使用调试模式时 ,如果/其他成本:

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

和三元运营商成本:

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 

使用发布模式时 ,如果/其他成本:

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

和三元运营商成本:

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

三元运算符比我机器上的if / else语句慢!

因此,根据不同的编译器优化技术,内部运算符和if / else的行为可能会大不相同。

  • C中一些更晦涩的运算符之所以存在,仅是因为它们允许将各种类似于函数的宏实现为返回结果的单个表达式。 我会说,这就是为什么主要目的?:,运营商被允许存在,尽管它们的功能是多余的,否则。

    假设我们希望实现一个类似于函数的宏,该宏返回两个参数中的最大值。 然后将其称为,例如:

     int x = LARGEST(1,2); 

    将其实现为类似函数的宏的唯一方法是

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

    if ... else语句将无法实现,因为它不会返回结果值。 注意)

  • ?:的另一个目的是在某些情况下实际上提高了可读性。 大多数情况下if...else更具可读性,但并非总是如此。 例如,使用长而重复的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; ... } 

    可以用可读性更高的方法代替

     switch(something) { case A: array[i] = (x == A) ? x : y; break; case B: array[i] = (x == B) ? x : y; break; ... } 
  • 请注意, ?: 永远不会if-else导致更快的代码。 这是由困惑的初学者创建的一个奇怪的神话。 在优化代码的情况下, ?:在大多数情况下具有与if-else相同的性能。

    如果有的话, ?:可能比if-else ,因为它带有强制的隐式类型提升,即使是将不使用的操作数也是如此。 但是?:永远不会比if-else快。


注意)现在当然有人会争论不解为什么不使用函数。 确实,如果可以使用函数,则它总是比类函数宏更可取。 但是有时候您不能使用函数。 例如,假设以上示例中的x在文件范围内声明。 初始化程序必须是一个常量表达式,因此它不能包含函数调用。 其他必须使用类函数宏的实际示例涉及使用_Generic或“ X宏”进行类型安全的编程。

三元= if-else的简单形式。 它主要用于可读性。

与...相同

if(0)
do();


if(0)
{
do();
}

暂无
暂无

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

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