[英]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 指针会使程序崩溃。
这个概念通常被称为“短路”
这里有两个单独的问题:
里面的条件顺序是否重要?
谁能向我解释为什么会这样?
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 )
然后排序是必不可少的,因为如果p
是NULL
, p->left
会导致运行时错误,但短路评估确保不会对其进行评估。
||
也会发生短路评估。 但在这种情况下,当左侧为true
时,表达式会被缩短。 所以在这两种情况下,操作数的顺序都很重要。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.