是的,这是一个家庭作业问题,但我已经完成了我的研究并对这一主题进行了相当深入的思考,并且无法解决这个问题。问题表明这段代码没有展示short-circuit behavior并询问原因。但它在我看来它确实表现出短路行为,所以有人可以解释为什么它没有?
在C:
int sc_and(int a, int b) {
return a ? b : 0;
}
在我看来,在a
为假的情况下,程序根本不会尝试评估b
,但我一定是错的。为什么程序在这种情况下甚至触及b
,当它没有必要时?
答案 0 :(得分:117)
这是一个棘手的问题。 b
是sc_and
方法的输入参数,因此将始终进行评估。换句话说,sc_and(a(), b())
会调用a()
并致电b()
(订单无法保证),然后使用传递给{{sc_and
的结果调用a(), b()
1}}。它与三元运算符本身无关,它绝对会短路。
<强>更新强>
关于为什么我称之为“诡计问题”:由于缺乏明确定义的背景,因此需要考虑“短路”问题。 (至少由OP复制)。许多人,只给出一个函数定义,假设问题的上下文询问函数的 body ;他们通常不认为这个功能本身就是一种表达。这就是&#39;诀窍&#39;问题;为了提醒您,在一般的编程中,尤其是在像C-like这样的语言中,通常会有许多规则例外,您无法做到这一点。例如,如果问题是这样的:
请考虑以下代码。从 main 调用时,sc_and exibit会出现短路行为:
a?b:0
很明显,您应该将int sc_and(int a, int b){
return a?b:0;
}
int a(){
cout<<"called a!"<<endl;
return 0;
}
int b(){
cout<<"called b!"<<endl;
return 1;
}
int main(char* argc, char** argv){
int x = sc_and(a(), b());
return 0;
}
作为一个运营商,并在您自己的特定于域的语言中进行操作,并评估是否对sc_and
的调用表现出像常规sc_and
那样的短路行为。我根本不认为这是一个技巧问题,因为很清楚你不应该专注于三元运算符,而应该把重点放在C / C ++上。 s函数调用机制(并且,我猜,它可以很好地引入后续问题,编写一个短路的&&
,这将涉及使用sc_and
而不是函数。
你是否称之为三元运算符本身的短路(或其他东西,如条件评估&#39;)取决于你对短路的定义,你可以阅读各种评论对此的想法。虽然我的确如此,但它与实际问题并不十分相关,或者为什么我称它为“诡计”。
答案 1 :(得分:42)
当声明
bool x = a && b++; // a and b are of int type
执行,如果操作数b++
评估为a
(短路行为),则不会评估false
。这意味着不会发生b
的副作用。
现在,看看函数:
bool and_fun(int a, int b)
{
return a && b;
}
并调用此
bool x = and_fun(a, b++);
在这种情况下,a
是true
还是false
,b++
将始终在函数调用期间进行评估,并且b
上的副作用将始终采用地点。
也是如此
int x = a ? b : 0; // Short circuit behavior
和
int sc_and (int a, int b) // No short circuit behavior.
{
return a ? b : 0;
}
答案 2 :(得分:19)
正如其他人已经指出的那样,无论什么作为两个参数传递给函数,它都会在传入时进行评估。这是在tenary操作之前的方式。
另一方面,这个
#define sc_and(a, b) \
((a) ?(b) :0)
会“短路”,因为此宏并不意味着函数调用,因此不会对函数的参数进行评估。
答案 3 :(得分:5)
编辑以更正@cmasters评论中注明的错误。
在
int sc_and(int a, int b) {
return a ? b : 0;
}
... return
ed表达式确实显示短路评估,但函数调用没有。
尝试拨打
sc_and (0, 1 / 0);
函数调用评估1 / 0
,虽然从未使用它,因此可能导致 - 除零错误。
ANSI C标准(草案)的相关摘录是:
2.1.2.3程序执行
...
在抽象机器中,所有表达式都按照指定的方式进行评估 语义学。实际的实现不需要评估部分内容 表达式,如果它可以推断出它的值没有被使用而且没有 产生所需的副作用(包括通过调用a引起的任何副作用) 功能或访问易失性对象。)
和
3.3.2.2函数调用
...
语义
...
在准备调用函数时,会对参数进行求值, 并为每个参数分配相应的值 参数。
我的猜测是每个参数都被计算为一个表达式,但是参数列表作为一个整体是而不是一个表达式,因此非SCE行为是必需的。
作为C标准深水表面的一个splasher,我很欣赏两个方面的正确看法:
1 / 0
是否会产生未定义的行为?P.S。
即使您转到C ++,并将sc_and
定义为inline
函数,您也 获取SCE。如果你将它定义为C宏,就像@alk那样,你肯定会。
答案 4 :(得分:4)
要清楚地看到三元运算短路,请尝试稍微更改代码以使用函数指针而不是整数:
int a() {
printf("I'm a() returning 0\n");
return 0;
}
int b() {
printf("And I'm b() returning 1 (not that it matters)\n");
return 1;
}
int sc_and(int (*a)(), int (*b)()) {
a() ? b() : 0;
}
int main() {
sc_and(a, b);
return 0;
}
然后编译它(即使几乎没有优化:-O0
!)。如果b()
返回false,您将看到a()
未执行。
% gcc -O0 tershort.c
% ./a.out
I'm a() returning 0
%
这里生成的程序集如下所示:
call *%rdx <-- call a()
testl %eax, %eax <-- test result
je .L8 <-- skip if 0 (false)
movq -16(%rbp), %rdx
movl $0, %eax
call *%rdx <- calls b() only if not skipped
.L8:
正如其他人正确指出的那样,问题就是让你专注于那些短路的三元运算符行为(称为条件评估&#39;)而不是呼叫时的参数评估(按值调用) )不要短路。
答案 5 :(得分:0)
C三元运算符永远不会短路,因为它只评估单个表达式 a (条件),以确定由表达式 b 和<给出的值strong> c ,如果可能返回任何值。
以下代码:
int ret = a ? b : c; // Here, b and c are expressions that return a value.
它几乎等同于以下代码:
int ret;
if(a) {ret = b} else {ret = c}
表达 a 可能由其他运营商组成,例如&amp;&amp;或||因为它们可能在返回一个值之前评估两个表达式,所以可能会短路,但这不会被视为三元运算符正在进行短路,但运算符中使用的运算符与常规if语句中的运算符一样。
<强>更新强>
关于三元运营商是短路运营商存在争议。该论点表示,任何不评估其所有操作数的运算符都会根据下面的注释中的@aruisdante进行短路。如果给出这个定义,那么三元运算符将是短路的,在这种情况下,这是我同意的原始定义。问题是术语“短路”最初用于允许这种行为的特定类型的操作符,而那些是逻辑/布尔操作符,原因只有那些是我将尝试解释的。 / p>
在文章Short-circuit Evaluation之后,短路评估仅涉及在语言中实现的布尔运算符,其中知道第一个操作数将使第二个不相关,这对于&amp;&amp; ; operator是第一个操作数 false ,对于|| operator是第一个操作数 true ,the C11 spec也在6.5.13逻辑AND运算符和6.5.14逻辑OR运算符中注明。
这意味着对于要识别的短路行为,您可能希望在运算符中识别它,如果第一个操作数与第二个操作数无关,则必须像布尔运算符一样评估所有操作数。这符合“逻辑短路”部分中MathWorks中短路的另一个定义所写的内容,因为短路来自逻辑运算符。
由于我一直试图解释C三元运算符,也称为三元运算符,只计算两个操作数,它计算第一个操作数,然后计算第二个操作数,其中两个中的一个取决于第一个的价值。它总是这样做,它不应该在任何情况下评估所有三个,所以在任何情况下都没有“短路”。
与往常一样,如果你发现某些事情是不对的,请写一个评论,反对这个而不仅仅是一个downvote,这只会让SO体验变得更糟,我相信我们可以成为一个更好的社区。只是赞成一个人不同意的答案。