简体   繁体   English

(C)有人可以向我解释为什么代码返回其结果吗?

[英](C) Can someone explain to me why the code returns what it does?

So I was talking a lynda course on learning the C language and this example was shown and barely explained so I was unable to understand why there results were what they were.Keep in mind the code isn't supposed to be correct, I'm just supposed to understand what happens. 所以我在讲一本关于学习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;
}

and the result is: 结果是:

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

Can someone explain to me why increment returns 47 because if a is int x and int x = 50 and b is 47 because it executes MAX(x, increment()) . 有人可以向我解释为什么增量返回47,因为如果aint xint x = 50b47,因为它执行MAX(x,递增()) If I'm not reading the code wrong it should print 50 because 50 is greater than 47. 如果我没有看错代码,则应打印50,因为50大于47。

As I see , this would be unspecified behaviour, as the order of execution / evaluation of function arguments is not specified. 如我所见,这将是未指定的行为,因为未指定函数参数的执行/评估顺序。

Quoting C11 , chapter §6.5.2.2, Function calls, ( emphasis mine ) 引用C11 ,第§6.5.2.2章,函数调用,( 重点是我的

There is a sequence point after the evaluations of the function designator and the actual arguments but before the actual call. 在函数指示符和实际参数的求值之后,但在实际调用之前,有一个顺序点。 Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function . 相对于被调用函数的执行,不确定地对调用函数中所有未包括在被调用函数的主体执行之前或之后特定排序的评估(包括其他函数调用)进行排序

This is a good example of the problem of combining side effects in a function with macros that evaluate parameters multiple times. 这是将函数中的副作用与可多次评估参数的宏结合在一起的一个很好的例子。

Leading to the first printf we see that increment is called twice from the two increment returns lines. 转到第一个printf,我们看到在两个增量返回行中两次调用了增量。 In this case, the MAX macro "sees" 50 and 47 and concludes that 50 is greater. 在这种情况下,MAX宏“看到” 50和47,并得出50更大的结论。 The printf then calls increment again so it "sees" 50 and 52 and the macro result of 50 然后,printf再次调用增量,以便“看到” 50和52以及宏结果50

For the second printf we see that increment is now called three times. 对于第二个printf,我们看到增量现在被调用了3次。 The MAX macro compares 50 and 57 and concludes 57 is greater, it then calls increment again to get the result of 62. Now back to printf, we print 50 and another increment call to get 67 and of course the macro result of 62. MAX宏将50与57进行比较,得出的结论是57更大,然后再次调用增量来获得62的结果。现在回到printf,我们打印50并进行另一个增量调用来获得67,当然还要返回62的宏结果。

This explains the bizarre output of this code. 这解释了此代码的奇怪输出。 The combination of side effects, multiple evaluation inside a macro and reliance on order of parameter evaluation make this truly awful code. 副作用,宏内部的多次求值以及对参数求值顺序的依赖使这些代码变得非常糟糕。 Worthy of an evil coding contest or a text book under the heading "Don't do this!" 值得参加邪恶的编码竞赛或教科书,标题为“不要这样做!”

There are three issues at play here. 这里有三个问题。

First is that your increment function changes state every time you call it - it will always return a different value for each call. 首先是您每次调用increment函数时都会更改状态-每次调用总是返回不同的值。 Second is that function arguments are not guaranteed to be evaluated left-to-right. 其次是不能保证函数参数从左到右进行求值。 Third is that after macro expansion, your printf call looks like this: 第三是宏扩展后,您的printf调用如下所示:

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

so it's possible for increment to be called 3 times. 因此可能会将increment称为3次。

Based on your output, increment is being called in this order: 根据您的输出, increment以下顺序调用increment

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

That is, the expression (x) > (increment()) ? (x) : (increment()) 也就是说,表达式(x) > (increment()) ? (x) : (increment()) (x) > (increment()) ? (x) : (increment()) is being evaluated first - increment() returns 47, which is not greater than x (50), so the result of the expression is 50. (x) > (increment()) ? (x) : (increment())首先被求值- increment()返回47,该值不大于x (50),因此表达式的结果为50。

Sometime after that, the lone increment() expression is called, which returns 52. 此后的某个时间,将调用单独的increment()表达式,该表达式返回52。

With the second printf call, we get 通过第二个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

Again, (x) > (increment()) ? (x) : (increment()) 同样, (x) > (increment()) ? (x) : (increment()) (x) > (increment()) ? (x) : (increment()) is evaluated first. (x) > (increment()) ? (x) : (increment())首先被评估。 This time, increment() is called twice , returning 57 in the test condition, then 62 in the result. 这次, increment() 两次被调用,在测试条件下返回57 ,然后在结果中返回62

It's then called a third time when the expression increment() is evaluated. 然后,在表达式expression increment()被求值时第三次调用它。

So... 所以...

The best way to deal with this is to assign the result of increment to a temporary, and use that temporary in the printf calls: 解决此问题的最佳方法是将increment结果分配给一个临时文件,并在printf调用中使用该临时文件:

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

Most operators in C don't force left to right evaluation. C语言中的大多数运算符都不强制从左到右进行评估。 The few that do are the logical && and || 少数是逻辑&&|| operators, the ?: ternary operator, and the , comma operator (which is not the same as the commas used in parameter lists for function calls). 运营商,该?:三元运算符,并且,逗号(这是一样的参数列出了用于函数调用的逗号)。

A variable within a function, that is declared with static qualifier will be statically allocated and while it's scope will be limited to this function (I think), it will have a lifetime of a global. 使用静态限定符声明的函数中的变量将被静态分配,并且其范围将限于该函数(我认为),但是它将具有全局寿命。 What this means is, it will retain it's value throughout function executions. 这意味着它将在整个函数执行过程中保留其价值。 It will only be initialized once. 它只会被初始化一次。

Therefore upon every invocation of increment(), the function will return value 5 higher than the last time, starting with 42+5=47. 因此,每次调用increment()时,该函数将返回比上次高的值5,从42 + 5 = 47开始。

What's interesting is how does a macro affect your program execution. 有趣的是,宏如何影响程序执行。 If MAX worked the way functions do, it would evaluate a and b expressions to integers or whatever other type it compares first. 如果MAX按照函数的方式工作,它将把a和b表达式求值为整数或首先比较的任何其他类型。 Then it would evaluate the a>b ? 然后它将评估a> b? a : b expression. a:b表达式。 However since it's a preprocessor macro, all it does is text substitution. 但是,由于它是一个预处理器宏,因此它所做的就是文本替换。 Resulting expression, hidden underneath confusing macro, is: 隐藏在混乱的宏下面的结果表达式是:

x > increment() ? x : increment()

Since increment() is not merely providing a value, but contains a side effect of changing it's static variable, it matters a lot whether it's evaluated once, or twice. 由于增量()不仅提供值,而且还包含更改其静态变量的副作用,因此,对它进行一次还是两次评估就很重要。

In this case upon reaching the second MAX macro, we compare x=50 with result of fourth invocation of increment(), ie. 在这种情况下,到达第二个MAX宏后,我们将x = 50与第四次调用increment()的结果进行比较。 62. As 50 < 62, we then evaluate the rightmost expression, fifth invocation of increment(), which returns 67, which is then returned by ?: operator to printf(). 62.当50 <62时,我们求出最右边的表达式,即第五次调用increment(),它返回67,然后由?:运算符返回给printf()。

Note: a line above, in first printf(), in ?: underlying operator of MAX macro the leftmost operand evaluated to true, so only the middle operator was evaluated, which was "x". 注意:在第一个printf()中的一行中:?:MAX宏的基础运算符,最左边的操作数评估为true,因此仅评估了中间运算符,即“ x”。 This is why first printf resulted in only two invocations of increment(), while the second printf() resulted in three such invocations. 这就是为什么第一个printf仅导致两次increment()的原因,而第二个printf()仅导致三个此类的调用。

edit: of course the remarks about this resulting in unspecified behavior are in the right. 编辑:当然,有关导致未指定行为的注释在右边。 Nevertheless we empirically see that in practice your compiler produced execution in line with inuitive notion of expected order in which expression in function call gets executed. 尽管如此,从经验上我们还是可以看到,在实践中,编译器根据预期顺序的启发式概念执行了函数调用中的表达式。

compile the source code with gcc -c -S gcc -c -S编译源代码

and you get a .s file which is assembly language . 您会得到一个.s文件,它是汇编语言

You can then see what is really happening in the .s file, and I suspect because #define MAX is a macro that when compiled it expands into inline code . 然后,您可以看到.s文件中实际发生的情况,我怀疑是因为#define MAX是一个 ,在编译时它会扩展为内联代码 I took C in college and it was years ago, this is the best answer i can give without scrutinizing the assembly language. 我上大学时读过C,那是几年前的事,这是我无需审查汇编语言就能给出的最佳答案。 For reasons such as this is why I avoid doing macros... because of how it can cause problems if you are not fully aware of how a compiler expands the macro into inline code. 由于诸如此类的原因,我避免使用宏...因为如果您不完全了解编译器如何将宏扩展为内联代码,那么它可能会引起问题。 While a macro can save you some typing of source code and may look slick, it's pointless if code execution produces the wrong answer. 尽管宏可以为您节省一些源代码的输入并且看起来很漂亮,但如果代码执行产生错误的答案,则毫无意义。

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

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