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

发布于 2024-07-17 00:39:51 字数 103 浏览 10 评论 0原文

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

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

悲念泪 2024-07-24 00:39:52

在 C 语言中,它的真正用途在于它是一个表达式,而不是一个语句; 也就是说,您可以将其放在语句的右侧 (RHS)。 这样你就可以把某些东西写得更简洁。

In C, the real utility of it is that it's an expression instead of a statement; that is, you can have it on the right-hand side (RHS) of a statement. So you can write certain things more concisely.

吾性傲以野 2024-07-24 00:39:52

给出的其他一些答案很棒。 但令我惊讶的是,没有人提到它可以用来以一种紧凑的方式帮助强制 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 版本更加紧凑​​,并且可以说更具可读性。

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.

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. The easiest alternative is to make n not a const, this would allow an ordinary if to initialize it. But if you want it to be const, it cannot be done with an ordinary 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.

时光沙漏 2024-07-24 00:39:52

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

此外,由于它是一个表达式,正如 Charlie Martin 所写,这意味着它可以出现在语句的右侧这对于简洁来说很有价值。

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.

妖妓 2024-07-24 00:39:52

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

Look->       See?!

No
:(
Oh, well
);

It's crucial for code obfuscation, like this:

Look->       See?!

No
:(
Oh, well
);
我喜欢麦丽素 2024-07-24 00:39:52

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

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

·深蓝 2024-07-24 00:39:52

C 中有很多东西在技术上并不需要,因为它们或多或少可以很容易地用其他东西来实现。 这是一个不完整的列表:

  1. while
  2. for
  3. 函数
  4. 结构

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

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. 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.

夜司空 2024-07-24 00:39:52

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

这不是一个很好的例子,但我在更好的事情上画了一个空白。 有一点是确定的,当你真正需要使用三元的时候并不经常,尽管我仍然经常使用它。

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;

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

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.

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.

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:

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.

叹倦 2024-07-24 00:39:52

由于尚未有人提到这一点,获得智能 printf 语句的唯一方法是使用三元运算符:

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

警告:当您从 C 迁移到 C++ 时,运算符优先级存在一些差异,并且可能会对由此产生的微妙错误感到惊讶。

Since no one has mentioned this yet, about the only way to get smart printf statements is to use the ternary operator:

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.

锦上情书 2024-07-24 00:39:52

事实上,三元运算符是一个表达式,而不是一个语句,因此可以将其用于用作表达式一部分的类函数宏的宏扩展中。 Const 可能不是原始 C 的一部分,但宏预处理器可以追溯到很久以前。

我看到它使用的一个地方是在一个数组包中,该包使用宏进行绑定检查数组访问。 检查引用的语法类似于 aref(arrayname, type, index),其中 arrayname 实际上是指向包含数组边界和数据的无符号 char 数组的结构的指针,类型为数据的实际类型,index是索引。 这个扩展非常麻烦(我不会凭记忆来做),但它使用了一些三元运算符来进行边界检查。

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

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

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

从数组中获取一个元素。 A
尝试时发生运行时错误
超出范围的引用
大批。 没有类型检查
给定位置的值
实际上是使用的类型
取消引用数组。

这里是 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++ 向量)甚至更加毛茸茸的,涉及多个嵌套的三元运算符。

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.

手心的海 2024-07-24 00:39:52

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

It's syntatic sugar and a handy shorthand for brief if/else blocks that only contain one statement. Functionally, both constructs should perform identically.

夏日落 2024-07-24 00:39:52

正如 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;
}

不同边界的成本有很大不同且很奇怪(请参阅原始材料)。 while if change:

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

to

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

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

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

但在我的台式机intel i5 cpu/windows 10/vs2015上,我的测试结果与msdn博客有很大不同。

使用调试模式时,if/else 成本:

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 

使用发布模式时,if/else 成本:

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 的行为可能有很大不同。

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.

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.

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!

so according to different compiler optimization techniques, ternal operator and if/else may behaves much different.

北笙凉宸 2024-07-24 00:39:52
  • C 中一些比较晦涩的运算符的存在仅仅是因为它们允许将各种类似函数的宏实现为返回结果的单个表达式。 我想说,这是允许 ?:, 运算符存在的主要目的,即使它们的功能在其他方面是多余的。

    假设我们希望实现一个类似函数的宏,它返回两个参数中最大的一个。 然后它将被调用,例如:

    int x = 最大(1,2); 
      

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

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

    使用 if ... else 语句是不可能的,因为它不返回结果值。 注意)

  • ?: 的另一个目的是它在某些情况下实际上提高了可读性。 大多数情况下,if...else 更具可读性,但并非总是如此。 以长的、重复的 switch 语句为例:

    开关(某事) 
      { 
        情况一:  
          如果(x==A) 
          { 
            数组[i] = x; 
          } 
          别的 
          { 
            数组[i] = y; 
          } 
          休息; 
    
        情况B:  
          如果(x==B) 
          { 
            数组[i] = x; 
          } 
          别的 
          { 
            数组[i] = y; 
          } 
          休息; 
        ... 
      } 
      

    这可以替换为更具可读性的

    开关(某事) 
      { 
        情况 A:数组[i] = (x == A) ?   x : y;   休息; 
        情况 B:数组 [i] = (x == B) ?   x : y;   休息; 
        ... 
      } 
      
  • 请注意 ?: 不会产生比 更快的代码if-else。 这是困惑的初学者创造的一些奇怪的神话。 在优化代码的情况下,?: 在绝大多数情况下提供与 if-else 相同的性能。

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


注意) 现在当然有人会争论并想知道为什么不使用函数。 事实上,如果您可以使用函数,它总是优于类似函数的宏。 但有时你不能使用函数。 例如,假设上例中的 x 是在文件范围内声明的。 初始值设定项必须是常量表达式,因此它不能包含函数调用。 其他必须使用类函数宏的实际示例涉及使用 _Generic 或“X 宏”进行类型安全编程。

  • 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".

心作怪 2024-07-24 00:39:52

三元 = if-else 的简单形式。 它主要是为了可读性而提供的。

ternary = simple form of if-else. It is available mostly for readability.

不即不离 2024-07-24 00:39:52

if(0)
do();


if(0)
{
do();
}

The same as

if(0)
do();


if(0)
{
do();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文