逻辑运算符的短路是强制的吗? 和评价顺序?

ANSI标准是否要求 C或C ++中的逻辑运算符被短路?

我很困惑,因为我记得K&R书上说你的代码不应该依赖这些被短路的操作,因为他们可能不会。 有人可以指出在标准的地方说逻辑运算总是短路? 我最感兴趣的是C ++,C的答案也会很棒。

我还记得阅读(不记得在哪里)评价顺序没有严格定义,所以你的代码不应该依赖或假定一个expression式中的函数将以特定的顺序执行:在声明的结尾,所有引用的函数将被调用,但编译器有自由select最有效的命令。

标准是否表明这个expression的评估顺序?

if( functionA() && functionB() && functionC() ) cout<<"Hello world"; 

是的,运营商||需要短路和评估顺序 和C&C ++标准中的&&

C ++标准说(在C标准中应该有一个等价的子句):

1.9.18

在评估下面的expression

 a && b a || b a ? b : c a , b 

使用这些expression式中操作符的内置含义,在第一个expression式 (12) 的评估之后有一个序列点

在C ++中还有一个额外的陷阱:短路不适用于重载运算符||types 和&&

脚注12:本段中指示的运算符是内置运算符,如第5节中所述。当其中一个运算符在有效上下文中被重载(第13章),从而指定用户定义的运算符函数时,expression式指定一个函数调用,操作数形成一个参数列表, 没有它们之间的隐含序列点。

通常不build议在C ++中重载这些运算符,除非您有特别的要求。 你可以这样做,但是它可能会破坏其他人的代码中的预期行为,特别是如果通过实例化模板来间接使用这些运算符,而这些运算符的types是重载的。

短路评估和评估顺序是C和C ++的强制性语义标准。

如果不是,这样的代码将不是一个常见的习惯用法

  char* pChar = 0; // some actions which may or may not set pChar to something if ((pChar != 0) && (*pChar != '\0')) { // do something useful } 

6.5.13节C99规范的逻辑AND运算符 (PDF链接)说

(4)。 与按位二进制&运算符不同,&&运算符保证了从左到右的评估; 在第一个操作数的评估之后有一个序列点。 如果第一个操作数比较等于0,则不计算第二个操作数。

同样的, 6.5.14逻辑OR运算符

(4)不像按位| 运算符|| 运营商保证从左到右的评估; 在第一个操作数的评估之后有一个序列点。 如果第一个操作数不等于0,则不计算第二个操作数。

类似的措词可以在C ++标准中find, 在这个草稿中查看5.14节 。 检查者在另一个答案中注意到,如果你重写&&或||,那么这两个操作数必须被评估,因为它变成了一个普通的函数调用。

是的,它要求(评估顺序和短路)。 在你的例子中,如果所有函数都返回true,那么调用的顺序严格来自functionA,然后是functionB,然后是functionC。 用于这个喜欢

 if(ptr && ptr->value) { ... } 

逗号运算符也是如此:

 // calls a, then b and evaluates to the value returned by b // which is used to initialize c int c = (a(), b()); 

一个在&& ,,的左右操作数之间说 ,以及?: (条件运算符)的第一个和第二个/第三个操作数之间是一个“序列点”。 在这一点之前,任何副作用都被完全评估。 所以,这是安全的:

 int a = 0; int b = (a++, a); // b initialized with 1, and a is 1 

请注意,逗号运算符不应与用于分隔事物的语法逗号混淆:

 // order of calls to a and b is unspecified! function(a(), b()); 

C ++标准在5.14/1说:

&&运算符组从左到右。 操作数都被隐式地转换为typesbool(子句4)。 如果两个操作数都为真,结果为真,否则为真。 不同于&& &&保证从左至右的评估:如果第一个操作数为假,则不评估第二个操作数。

而在5.15/1

|| 操作员组从左到右。 操作数都隐式转换为bool(第4章)。 如果其中的任何一个操作数为true,则返回true,否则返回false。 不像|,|| 保证从左到右的评估; 此外,如果第一个操作数的计算结果为true,则不计算第二个操作数。

它在旁边说:

结果是一个布尔。 第一个expression式的所有副作用除了临时对象(12.2)的破坏发生在第二个expression式被评估之前。

除此之外, 1.9/18

在评估每个expression式

  • a && b
  • a || b
  • a ? b : C
  • a , b

使用这些expression式(5.14,5.15,5.16,5.18)中操作符的内置含义,在第一个expression式的求值之后有一个序列点。

直接从良好的老K&R:

C保证&&|| 从左到右进行评估 – 我们很快就会看到这个问题。

要非常小心。

对于PODtypes,这些是快捷操作符。

但是如果你为自己的类定义这些运算符,那么它们并不是捷径。 由于在这些不同情况下的使用语义差异,build议您不要定义这些运算符。

对于运算符&&和运算符|| 对于PODtypes,评估顺序是从左到右(否则短切割将是困难的:-)但是对于您定义的重载操作符,这些基本上是定义方法的语法糖,因此参数的评估顺序是不确定的。

如果您信任维基百科:

[ &&|| ]在语义上不同于按位运算符&和| 因为如果结果可以从左边单独确定,他们将永远不会评估正确的操作数

http://en.wikipedia.org/wiki/C_(programming_language)#Characteristics

你的问题归结为C ++运算符的优先级和相关性。 基本上,在具有多个运算符且没有括号的expression式中,编译器遵循这些规则来构造expression式树。

对于优先级,当你有类似于A op1 B op2 C东西时,你可以将它们分组为(A op1 B) op2 CA op1 (B op2 C) 。 如果op1具有比op2更高的优先级,则会得到第一个expression式。 否则,你会得到第二个。

为了相关性,当你有类似于A op B op C东西的时候,你可以再次把组合成(A op B) op CA op (B op C) 。 如果op具有结合性,我们就得到第一个expression式。 如果它具有正确的结合性,我们最终会得到第二个结果。 这也适用于优先级相同的运营商。

在这个特殊情况下, &&优先级高于|| ,所以expression式将被评估为(a != "" && it == seqMap.end()) || isEven (a != "" && it == seqMap.end()) || isEven

订单本身是expression式树forms的“从左到右”。 所以我们首先评估a != "" && it == seqMap.end() 。 如果这是真的,整个expression是真实的,否则我们去isEven 。 该过程当然在左子expression式中recursion地重复。


有趣的花絮,但优先的概念有其math符号的根源。 同样的事情发生在a*b + c ,其中*优先级高于+

更加有趣/不明显的是,对于一个不加强调的expression式A1 op1 A2 op2 ... opn-1 An ,其中所有运算符具有相同的优先级,我们可以形成的二进制expression式树的数目由所谓的加泰罗尼亚数字给出 。 对于大n ,这些增长速度非常快。 d