三元运算符 ?: 与 if...else 的区别

92

在C++中,?:运算符比if()...else语句更快吗?它们在编译后的代码中有什么区别吗?


这是一个困难的问题,因为它还取决于编译器的优化设置。 - extraneon
3
这当然取决于您在分支内部做了什么。条件运算符只允许表达式,而“if”语句则允许语句。 - Gumbo
4
相关:三元运算符还是不用? - Nick Dandoulakis
12
有个人随意修改了我三年前提出的问题,重新编写了问题使其完全不像是我提出的,并且增加了一些完全不必要的代码,这使得整个问题变得毫无意义。由于常数折叠,这两个示例都简化为“result = 5”。我将进行还原操作。 - Xirdus
汇编版本cmov与jmp https://dev59.com/SWYq5IYBdhLWcg3w30Xc - Ciro Santilli OurBigBook.com
14个回答

110

并不会更快。只有当你可以根据某些表达式初始化常量变量时才有一点区别:

const int x = (a<b) ? b : a;

你不能使用 if-else 来实现同样的功能。


22
@开发者 Art:这在const变量中是不可能的。 - mtvec
1
你可以创建一个非const变量,在if/else中对其进行赋值,然后创建一个新的const变量,并使用非const变量来构造它。虽然有些浪费,但并非不可能。 - Puppy
7
那么好老的 max 呢?const int x = max(a,b); 完全可以正常运行。 - bobobobo
3
@bobobobo 哈!当我看到你的评论时,我以为你建议的命令是 max ? const int x = max(a,b);,然后我想,哇!这是什么鬼!后来我再看一遍,发现问号并不是等宽字体!考虑到主题,我认为我有理由认为问号是命令的一部分! :) - dewd
2
你可以使用 const int x = [&] -> int { if (a < b) return b; else return a; } - L. F.
显示剩余8条评论

94

根据你所使用的编译器而定,但在任何现代编译器上通常没有区别。这是你不必担心的事情。要专注于代码的可维护性。


1
对于许多应用程序而言,即使在非常低效的编译器上,性能差异也不值得考虑。+1 - user395760
4
关于代码的可维护性,我更喜欢使用if...else。至少对我来说,这样更容易阅读。 - Exa
3
@Exa:取决于上下文。 当您初始化对象时,三元运算符通常更好。 - Nemanja Trifunovic
2
@kotlinski,我并不是说条件语句比if语句更难维护。它们在不同的情况下都更清晰,正如上面链接的“使用三元运算符还是不使用”问题的答案所描述的那样。 - ptomato
我发现了一个关于三元选择的示例,它阻止了移动构造的使用,导致与if / else相比多出额外的拷贝。 - Christopher Johnson
显示剩余4条评论

47

我曾见过GCC将条件运算符转换为cmov(条件移动)指令,而将if语句转换为分支,这意味着在我们的情况下使用条件运算符时代码更快。但那是几年前的事了,如今两者编译后很可能生成相同的代码。

不能保证它们会编译成相同的代码。如果需要性能,就像往常一样,测量。当你测量并发现:1. 你的代码太慢了;2. 这段特定的代码是罪魁祸首时,就要研究编译器生成的汇编代码,并自行检查发生了什么。

不要相信黄金法则,比如“如果我使用条件运算符,编译器总会生成更高效的代码”。


4
当我使用GCC开发PS3时,使用条件语句而不是“if”语句有助于避免分支。 - Johan Kotlinski
这是否仅适用于 C 语言?C++ 标准规定:“仅计算第二个和第三个表达式中的一个。与第一个表达式关联的每个值计算和副作用都先于第二个或第三个表达式关联的每个值计算和副作用。” 这显然会防止编译器生成 cmove 指令。 - Joey.Z
3
@zoujyjs 不,C语言也有同样的规则。但是根据“as-if”规则,只要最终结果正确,编译器就可以自由地进行优化。因此,只要没有副作用,编译器就可以进行这种优化。 - jalf
如何使用cmov实现if else?一个mov到值1 +一个cmov到值2? - Ciro Santilli OurBigBook.com
1
注意:此建议已过时(约2010年),我无法在gcc 4.4或更高版本上重现此问题。 - ACyclic

17

它们是相同的,然而三元运算符可以用于难以使用if/else的地方:

printf("Total: %d item%s", cnt, cnt != 1 ? "s" : "");

使用if/else语句会生成非常不同的编译代码。


8年后的更新...

实际上,我认为这样做会更好:

printf(cnt == 1 ? "Total: %d item" : "Total: %d items", cnt);

(实际上,我非常确定你可以将第一个字符串中的“%d”替换为“one”)


8
甚至不需要三元运算符:printf("Total: %d item%s", cnt, "s" + (cnt==1)); - MSalters
@MSalters 但这会在字符串末尾产生双重空值,这可能会在其他情况下造成问题,其中双重空值表示某些含义(例如,在OPENFILENAME结构lpStrFilter成员中)。 - bobobobo
1
@bobobobo:不是的。%s 打印源字符串中的内容,但不包括\0 - MSalters
@MSalters 这个 printf("Total: %d item%s", cnt, "s" + (cnt==1)); 是如何工作的? - Quirk
3
@Quirk:(cnt==1) 是真或假,它转换为 0 或 1。 "s" 是指向以空字符结尾的字符串的指针。加一会跳过一个字符 (即s)。这样打印出来的结果可能是 "s" 或 ""。 - MSalters
@MSalters 我真不想审查你的代码。我得仔细思考所有的技巧!虽然很厉害,但我不希望我的初级开发人员这样做! - Azeroth2b

4
无论编译后的代码如何,它们在语义上是不同的。 <cond>?<true expr>:<false expr> 是一个表达式,而 if..else.. 是一个语句。
尽管条件表达式的语法看起来很奇怪,但这是一件好事。你被迫提供一个 <false expr> 并对两个表达式进行类型检查。
在基于表达式和函数的语言(例如Lisp,Haskell)中,? : 在C++中相当于 if..else.. 语句。

3

只是为了稍微有些另类...


这段话与IT技术无关。
x ? y : x = value

如果x不为0(false),则将value赋值给y


3
不加括号的话:https://dev59.com/a2s05IYBdhLWcg3wB9af该链接讨论了在使用条件运算符和赋值运算符时,运算符优先级可能会导致意料之外的行为。建议在这些情况下使用括号以明确优先级。 - Jerry Jeremiah
1
错误的(请参见https://dev59.com/a2s05IYBdhLWcg3wB9af#7499505)。此外,这与问题有什么关系? - Antonio

1

您不必把所有内容都放在一行上:

x = y==1 ?
    2
    :// else
    3;

这种方式比if/else更清晰,因为你可以立即看到两个分支都会将x赋值。

你还可以声明一个常量,例如

int const x = y==1 ?
            2
            :// else
            3;

而 const 可以对编译器有用,使代码更加优化。


0
我认为在某些情况下,内联if语句可以产生更快的代码,因为它的作用范围。对象的创建和销毁可能是昂贵的,所以请考虑以下场景:
class A{
    public:
    A() : value(0) {
        cout << "Default ctor" << endl;
    }
    A(int myInt) : value(myInt)
    {
        cout << "Overloaded ctor" << endl;
    }

    A& operator=(const A& other){
        cout << "= operator" << endl;
        value = other.value; 
    }

    ~A(){
        cout << "destroyed" << std::endl;
    }

    int value;

};


int main()
{
   {
       A a;
       if(true){
           a = A(5);
       }else{
           a = A(10);
       }
   }

   cout << "Next test" << endl;
   {
        A b = true? A(5) : A(10);
   }
   return 0;
}

使用这段代码,输出将会是:
Default ctor                                                                                                                                                                                                                      
Overloaded ctor                                                                                                                                                                                                                   
= operator                                                                                                                                                                                                                        
destroyed                                                                                                                                                                                                                         
destroyed                                                                                                                                                                                                                         
Next test                                                                                                                                                                                                                         
Overloaded ctor                                                                                                                                                                                                                   
destroyed  

所以通过内联 if,我们可以节省许多操作,这些操作需要在与 b 相同的作用域中保持 a 的活动状态。虽然条件评估速度在两种情况下都很可能相当快,但改变作用域会迫使您考虑其他因素,而内联 if 可以避免这种情况。

那么 A a(true ? 5 : 10); 怎么样? - Quest

0
在C语言中,有一个三目运算符“?:”可用于构建形式为的条件表达式。
exp1 ? exp2:exp3

其中 exp1、exp2 和 exp3 是表达式

例如

        a=20;
        b=25;
        x=(a>b)?a:b;

        in the above example x value will be assigned to b;

这可以使用if..else语句编写如下:

            if (a>b)
             x=a;
             else
             x=b;

因此,这两者之间没有区别。这是为了让程序员更容易编写,但对于编译器来说,它们是相同的。


0

我认为在大多数编译器和目标平台上,使用"if"语句和三目运算符"?:"都会有快慢之分。两种形式的紧凑程度也会因情况而异。哪种形式更优取决于编译器和平台。如果你正在嵌入式微控制器上编写性能关键代码,请查看每种情况下编译器生成的代码,并确定哪种更好。在“主流”PC上,由于缓存问题,唯一确定哪种更好的方法是在类似实际应用程序的环境中对两种形式进行基准测试。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接