为什么C版本的逻辑AND不显示短路行为?

时间:2014-10-13 15:17:17

标签: c short-circuiting

是的,这是一个家庭作业问题,但我已经完成了我的研究并对这一主题进行了相当深入的思考,并且无法解决这个问题。问题表明这段代码没有展示short-circuit behavior并询问原因。但它在我看来它确实表现出短路行为,所以有人可以解释为什么它没有?

在C:

int sc_and(int a, int b) {
    return a ? b : 0;
}

在我看来,在a为假的情况下,程序根本不会尝试评估b,但我一定是错的。为什么程序在这种情况下甚至触及b,当它没有必要时?

6 个答案:

答案 0 :(得分:117)

这是一个棘手的问题。 bsc_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++);

在这种情况下,atrue还是falseb++将始终在函数调用期间进行评估,并且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体验变得更糟,我相信我们可以成为一个更好的社区。只是赞成一个人不同意的答案。