逻辑运算符的短路是强制的吗? 和评价顺序?
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 C
或A op1 (B op2 C)
。 如果op1
具有比op2
更高的优先级,则会得到第一个expression式。 否则,你会得到第二个。
为了相关性,当你有类似于A op B op C
东西的时候,你可以再次把组合成(A op B) op C
或A 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