(C)有人可以向我解释为什么代码会返回它的作用吗?

时间:2017-01-12 17:57:13

标签: c parameter-passing function-calls

所以我正在讲一个关于学习C语言的lynda课程,这个例子已经展示并且几乎没有解释,所以我无法理解为什么结果是它们的原因。请记住代码不应该是是的,我只是想了解会发生什么。

#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;
}

结果是:

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

有人可以向我解释为什么增量会返回47,因为如果 a int x int x = 50 b < / strong> 47 ,因为它执行 MAX(x,increment())。如果我没有读错代码,则应打印50,因为50大于47。

5 个答案:

答案 0 :(得分:2)

如我所见,这将是未指定的行为,因为未指定执行/评估函数参数的顺序。

引用C11,章节§6.5.2.2,函数调用,(强调我的

  

在评估函数指示符和实际值之后有一个序列点   参数但在实际调用之前。 调用函数中的每个评估(包括   其他函数调用)在其他方式之前或之后没有特别排序   被调用函数体的执行是相对于不确定地排序的   执行被调用函数

答案 1 :(得分:1)

这是将函数中的副作用与多次计算参数的宏组合的问题的一个很好的例子。

通向第一个printf,我们看到从两个增量返回行调用两次增量。在这种情况下,MAX宏“看到”50和47并得出50更大的结论。然后printf再次调用增量,因此它“看到”50和52以及50

的宏结果

对于第二个printf,我们看到增量现在被调用三次。 MAX宏比较50和57,结束57更大,然后再次调用增量得到62的结果。现在回到printf,我们打印50和另一个增量调用得到67,当然宏结果是62. < / p>

这解释了此代码的奇怪输出。副作用的组合,宏内部的多重评估以及对参数评估顺序的依赖使得这个真正糟糕的代码成为可能。值得一个邪恶的编码比赛或标题为“不要这样做!”的教科书。

答案 2 :(得分:1)

这里有三个问题。

首先,您的increment函数每次调用时都会更改状态 - 始终会为每次调用返回不同的值。其次,函数参数不能保证从左到右进行评估。第三是在宏扩展之后,您的printf电话看起来像这样:

printf("max of %d and %d is %d\n", x,increment(), (x) > (increment()) ? (x) : (increment()));

因此increment可以被调用3次。

根据您的输出,按此顺序调用increment

printf("max of %d and %d is %d\n", x, increment(), (x) > (increment()) ? (x) : (increment()));
                                      ^                   ^
                                      |                   |
                                      |                   +---- increment returns 47
                                      +------------------------ increment returns 52

即,首先评估表达式(x) > (increment()) ? (x) : (increment()) - increment()返回47,其不大于x(50),因此表达式的结果为50. / p>

在此之后的某个时间,调用单独的increment()表达式,返回52。

通过第二次printf调用,我们得到

printf("max of %d and %d is %d\n", x, increment(), (x) > (increment()) ? (x) : (increment()));
                                      ^                   ^                     ^
                                      |                   |                     +---- increment returns 62
                                      |                   +-------------------------- increment returns 57
                                      +---------------------------------------------- increment returns 67

再次,首先评估(x) > (increment()) ? (x) : (increment())。这一次,increment()被称为两次,在测试条件中返回57,然后在结果中返回62

然后在评估表达式increment()时第三次调用它。

因此...

最佳处理此问题的方法是将increment的结果分配给临时,并在printf调用中使用该临时值:

int tmp = increment();
printf( "max of %d and %d is %d\n", x, tmp, MAX(x, tmp) );

C中的大多数操作员都没有强制进行正确的评估。少数几个是逻辑&&||运算符,?:三元运算符和,逗号运算符(不是与函数调用的参数列表中使用的逗号相同。

答案 3 :(得分:0)

使用 static 限定符声明的函数中的变量将被静态分配,而它的范围将限于此函数(我认为),它将具有全局的生命周期。这意味着,它将在整个函数执行期间保留它的价值。它只会被初始化一次。

因此,每次调用increment()时,函数将返回比上一次更高的值5,从42 + 5 = 47开始。

有趣的是宏如何影响您的程序执行。如果MAX按照函数的方式工作,它会将a和b表达式计算为整数或其首先比较的其他类型。然后它会评估a&gt; b? a:b表达式。但是,因为它是预处理器宏,所以它只是文本替换。隐藏在令人困惑的宏下面的结果表达式是:

x > increment() ? x : increment()

由于increment()不仅仅提供一个值,而且包含更改它的静态变量的副作用,因此无论是一次还是两次评估都很重要。

在这种情况下,在到达第二个MAX宏时,我们将x = 50与increment()的第四次调用结果进行比较,即。 62.因为50&lt; 62,然后我们评估最右边的表达式,第五次调用increment(),返回67,然后由?:operator返回到printf()。

注意:上面一行,在第一个printf()中,在?:MAX宏的底层运算符中,最左边的操作数计算为true,因此只评估了中间运算符,即“x”。这就是为什么第一个printf只导致两次increment()调用,而第二个printf()导致三次这样的调用。

编辑:当然,关于此的评论导致未指定的行为在右边。然而,我们凭经验看到,在实践中,您的编译器根据预期顺序的直观概念生成执行,其中函数调用中的表达式被执行。

答案 4 :(得分:0)

使用branch_2__child

编译源代码

您将获得一个{strong}汇编语言gcc -c -S文件。

然后,您可以看到.s文件中发生了什么,我怀疑是因为.s,在编译时会扩展为内联代码即可。 我在大学读了C,就在几年前,这是我能给出的最好的答案,而不需要仔细检查汇编语言。出于这样的原因,这就是为什么我要避免做宏...因为如果你不完全了解编译器如何将宏扩展为内联代码,它会如何导致问题。虽然宏可以为您节省一些源代码的输入并且可能看起来很光滑,但如果代码执行产生错误的答案则毫无意义。