繁体   English   中英

里面的条件顺序是否重要?

[英]Does the order of condition inside if matter?

例如在这段代码中:

node* find(node *cur, int id){
    node* p;

    if (cur->id==id) {
      printf("\n%d found at %p\n", id, cur);
      return cur;
    }
    else {
        if (cur->left!=NULL)  p=find(cur->left, id);
        if ((p==NULL) && (cur->right!=NULL)) p=find(cur->right, id);
    }
}

这个 function 的目的是返回树中节点的地址,其 id 等于输入 id。
我主要有:

while (1){
        fscanf(fi, "%d", &id);

        if (id==-2) break;

        fscanf(fi, "%d %d", &left, &right);

        cur=find(root, id);

        printf("%p\n", cur);

        if (cur==NULL) continue;

        printf("%d\n\n", cur->id);

        if (left!=-1) cur->left=makeNode(left);
        if (right!=-1) cur->right=makeNode(right);
    }

注意部分

cur=find(root, id);
printf("%p\n", cur);
if (cur==NULL) continue;
printf("%d\n\n", cur->id);

如果我改变:

if ((p==NULL) && (cur->right!=NULL))
  p=find(cur->right, id);

至:

if ((cur->right!=NULL) && (p==NULL))
  p=find(cur->right, id);
  

我会得到错误的答案,即
if ((p==NULL) && (cur->right,=NULL)) p=find(cur->right; id); , 我有:

2 在 0x56552970dcd0 找到
0x56552970dcd0
2
4 在 0x56552970dcf0 找到
0x56552970dcf0
4

但是if ((cur->right,=NULL) && (p==NULL)) p=find(cur->right; id); , 我有

2 在 0x564024e5ccd0 找到
0x564024e5ccf0
4
4 在 0x564024e5ccf0 找到
0x564024e5ccf0
4

谁能向我解释为什么会这样? 对不起,很长的帖子和我糟糕的英语。 提前致谢。

p未初始化。 这里对它的每一个引用都是未定义的行为。

您应该从node* p = NULL开始以获得一致的 output。

提示:始终在使用变量之前对其进行初始化。 每当你声明某事时,养成立即赋值的习惯。 如果您声明变量是否以及何时使用,这很少是一件麻烦事。

其他答案已经指出了您的代码中的错误。

我将讨论这部分:

里面的条件顺序是否重要?

是的,顺序很重要。 我们经常在我们的代码中使用这个事实。

C 只评估子条件,直到它知道最终结果。

所以考虑:

if (sub-cond1 && sub-cond2) {...}

如果sub-cond1给出结果 FALSE,则整个条件的结果将为 FALSE。 因此sub-cond2永远不会被评估。

if (sub-cond1 || sub-cond2) {...}

如果sub-cond1给出的结果为 TRUE,则整个条件的结果将为 TRUE。 因此sub-cond2永远不会被评估。

一个实际使用的例子:

int* p = foo(...);
if (p != NULL && p->data == 42) {...}

现在如果foo返回NULL子条件p != NULL将是 FALSE。 因此, p->data == 42永远不会被评估,这非常好,因为取消引用 NULL 指针会使程序崩溃。

这个概念通常被称为“短路”

这里有两个单独的问题:

  1. 里面的条件顺序是否重要?

  2. 谁能向我解释为什么会这样?

2的答案与1无关。错误的 output 是由于初始化p失败:

node* p = NULL ;

关于 1,尽管它不是 bug 的原因,但值得回答,因为它是一个更有趣的问题。

有很多事情需要考虑:

  • 短路评估
  • 副作用
  • 表达复杂度
  • 给定第一个表达式的第二个表达式的有效性。

对于表达式:

<expr_A> && <expr_B>

如果<expr_A>false ,则不计算<expr_B> false短路计算),因为无论 .

如果<expr_B>具有必须评估的副作用,这可能是一个问题。 如果您交换表达式<expr_B>始终被评估,并且结果行为可能会有所不同。 例如:

if( x && printf( "%s", s ) > 4 )

只有当x为真时才会打印字符串s ,同时:

if( printf( "%s", s ) > 4 && x )

无论如何都会打印s ,而条件的结果将保持不变。

要考虑的另一件事是表达式的复杂性。 在您的示例中:

if( p == NULL && cur->right != NULL )

右侧的子表达式涉及cur的获取和取消引用、成员访问,然后是比较,而左侧只是p的获取和比较。 因此,通过将更复杂的表达式放在右侧或&&上,您可以从短路评估中获得最佳性能优势。

最后,当您取消引用指针时,可能会出现右边的有效性问题,例如左边。 例如:

if( p != NULL && p->left != NULL )

然后排序是必不可少的,因为如果pNULLp->left会导致运行时错误,但短路评估确保不会对其进行评估。

||也会发生短路评估。 但在这种情况下,当左侧为true时,表达式会被缩短。 所以在这两种情况下,操作数的顺序都很重要。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM