我正在努力学习C / C ++的基础知识。我正在Lynda.com上学习课程
我的问题涉及第4章和第34章中的一系列代码;来自C / C ++基础培训课程的宏观警告"。我已经按照所有设置过程在PC上的Mac和Eclipse上正确设置Xcode和Eclipse。当我在MAC和PC上运行此代码时,我得到不同的结果。只是试图理解为什么会发生这种情况以及我能做些什么才能在两者上获得相同的结果。
以下是代码:
// working.c by Bill Weinman <http://bw.org/>
#include <stdio.h>
#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
int increment() {
static int i = 42;
i += 5;
printf("increment returns %d\n", i);
return i;
}
int main( int argc, char ** argv ) {
int x = 50;
printf("max of %d and %d is %d\n", x, increment(), MAX(x, increment()));
printf("max of %d and %d is %d\n", x, increment(), MAX(x, increment()));
return 0;
}
在PC上我得到了这个结果:
increment returns 47
increment returns 52
max of 50 and 52 is 50
increment returns 57
increment returns 62
increment returns 67
max of 50 and 67 is 62
在MAC(Xcode和Eclipse)上我得到了这个结果:
increment returns 47
increment returns 52
increment returns 57
max of 50 and 47 is 57
increment returns 62
increment returns 67
increment returns 72
max of 50 and 62 is 72
为什么会发生这种情况,我该怎么做才能确保结果相同?
答案 0 :(得分:8)
您在此处有未指定的结果。
printf()
内的评估顺序未定义。
一旦你在同一个printf中有多个increment()
个调用,你就不会知道哪个曾经先执行过,以及它们是如何被评估的。
答案 1 :(得分:6)
未定义全表达式中所有元素的计算顺序。所需要的只是每个子表达式在评估之前完全评估其操作数。在函数调用的情况下,可以按任何顺序计算参数,事实上,在另一个参数被完全评估时,一个参数可能只是部分评估。
首先,让我们扩展宏:
printf("max of %d and %d is %d\n", x,
increment(),
((x) > (increment()) ? (x) : (increment()));
(糟糕,此处还有另一个问题:如果increment()
大于x
,则会再次调用它。将MAX
宏改为函数,这样参数只会被评估一次!)
以下所有序列都是可能的。我在这里省略了对x
的评价,因为它没有改变。
increment()
,然后是x > increment()
,最后选择?:
个操作数。 (这可能是你期待的顺序。)x > increment()
,然后选择?:
操作数,最后是第二个参数increment()
。x > increment()
,然后是第二个参数increment()
,最后是选择了?:
个操作数。这些可能会产生不同的结果,它们都是对您的代码的正确解释。
当您在单个完整表达式中调用多个函数时,应确保这些函数不会产生任何副作用,或者每个函数的副作用不会改变任何函数的行为。其他功能。否则,在不同的编译器(或同一编译器的不同版本!)上进行编译可能会改变结果。
作为一个额外的例子,即使是外观简单的表达式increment() > increment()
也有一个未指定的结果,因为没有定义评估操作数的顺序;如果首先计算左操作数,那么结果将为false,否则将为真。
在更复杂的示例((a + b) * (c + d))
中,编译器可以按任意顺序评估a
,b
,c
和d
。所有必需的是a
和b
必须在a + b
之前进行评估,c
和d
必须在c + d
之前进行评估是的,a + b
和c + d
必须在最终的运算符*
之前进行评估。
答案 2 :(得分:2)
这里有两个问题。
您依赖编译器选择的任何内容来评估printf()的参数。首先评估的语句修改后面语句的值。将increment()调用移出参数列表。将increment()的结果存储在变量中,并将这些变量传递给printf()。
此外,MAX的宏扩展可以导致任一参数被评估一次或两次。因此,即使在相同的操作系统和编译器上,您也可能获得尴尬的结果。要解决此问题,请执行与我建议的存储increment()结果相同的操作。将这些变量传递给MAX()。