简体   繁体   English

宏和后增量

[英]Macros and postincrement

Here's some more weird macro behavior I was hoping somebody could shed light on: 这里有一些奇怪的宏观行为我希望有人可以阐明:

#define MAX(a,b) (a>b?a:b)

void main(void)
{
  int a = 3, b=4;

  printf("%d %d %d\n",a,b,MAX(a++,b++));
}

The output is 4 6 5. The value of b is incremented twice but not before MAX displays its value. 输出为4 6 5. b的值增加两次,但在MAX显示其值之前不增加。 Can anybody please tell me why this is happening and how does one predict such behavior? 任何人都可以告诉我为什么会这样,以及如何预测这种行为? (Another instance of why macros should be avoided!) (应该避免使用宏的另一个例子!)

Macros do text substitution. 宏进行文本替换。 Your code is equivalent to: 您的代码相当于:

printf("%d %d %d\n",a,b, a++ > b++ ? a++ : b++);

This has undefined behavior, because b is potentially incremented (at the end of the third argument) and then used (in the second argument) without an intervening sequence point. 这具有未定义的行为,因为b可能会增加(在第三个参数的末尾),然后在没有插入序列点的情况下使用(在第二个参数中)。

But as with any UB, if you stare at it for a while you might be able to come up with an explanation of what your implementation has actually done to yield the result you see. 但是和任何UB一样,如果你盯着它看一段时间,你可能会想出你的实现已经实现了什么来解释你看到的结果。 Order of evaluation of arguments is unspecified, but it looks to me as though the arguments have been evaluated in right-to-left order. 参数的评估顺序是未指定的,但它看起来好像参数已经按从右到左的顺序进行了评估。 So first, a and b are incremented once. 首先, ab递增一次。 a is not greater than b , so b is incremented again and the result of the conditional expression is 5 (that is to say, b after the first increment and before the second). a不大于b ,因此b再次递增,并且条件表达式的结果为5 (也就是说,在第一个增量之后和第二个增量之前的b )。

This behavior is not reliable - another implementation or the same implementation on another day might give different results due to evaluating the arguments in a different order, or theoretically might even crash because of the sequence point issue. 这种行为是不可靠的 - 另一个实现或另一天的相同实现可能会因为以不同的顺序评估参数而给出不同的结果,或者理论上甚至可能因序列点问题而崩溃。

In macro, parameters are just replaced by the arguments; 在宏中,参数只是被参数替换; so arguments can be evaluated multiple times if they are present multiple times in the macro. 因此,如果在宏中多次出现参数,则可以多次计算它们。

Your example: 你的例子:

MAX(a++,b++)

Expands to this: 扩展到这个:

a++>b++?a++:b++

I think you don't need more explanations :) 我想你不需要更多的解释:)

You can prevent this by assigning each parameter to a temporary variable: 您可以通过将每个参数分配给临时变量来防止这种情况:

#define MAX(a,b) ({   \
    typeof(a) _a = a; \
    typeof(b) _b = b; \
    a > b ? a : b;    \
})

(This one uses several GCC extensions, though) (但这个使用了几个GCC扩展)

Or use inline functions: 或使用内联函数:

int MAX(int a, int b) {
    return a > b ? a : b;
}

This will be as good as a macro at runtime. 这将在运行时与宏一样好。

Or don't do the increments in the macro arguments: 或者不要在宏参数中进行增量:

a++;
b++;
MAX(a, b)

When the preprocessor reads the line it replace the MAX(a++,b++) in the printf to the (a++>b++?a++;b++) 当预处理器读取该行时,它将printf中的MAX(a ++,b ++)替换为(a ++> b ++?a ++; b ++)

So your function becomes 所以你的功能变成了

    printf(a,b,(a++>b++?a++;b++));

Here order of evaluation is "compiler dependent". 评估顺序是“依赖于编译器”。

To understand when these conditions can occur u have to understand about Sequence point. 要了解何时可能发生这些情况,您必须了解序列点。

At each sequence point, the side effects of all previous expressions will be completed(all the variable calculation will be completed). 在每个序列点,将完成所有先前表达式的副作用(所有变量计算将完成)。 This is why you cannot rely on expressions such as: 这就是为什么你不能依赖于以下表达式的原因:

    a[i] = i++;

because there is no sequence point specified for the assignment, increment or index operators, you don't know when the effect of the increment on i occurs. 因为没有为赋值,增量或索引运算符指定序列点,所以不知道增量对i的影响何时发生。 “Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. “在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的计算来修改一次。 Furthermore, the prior value shall be read only to determine the value to be stored.”. 此外,先前的值应该是只读的,以确定要存储的值。“ If a program breaks these rules, the results on any particular implementation are entirely unpredictable(undefined). 如果程序违反了这些规则,则任何特定实现的结果都是完全不可预测的(未定义)。

--The sequence points laid down in the Standard are the following: - 标准中规定的顺序点如下:

1) The point of calling a function, after evaluating its arguments. 1)在评估其参数之后调用函数的要点。

2) The end of the first operand of the && operator. 2)&&运算符的第一个操作数的结尾。

3)The end of the first operand of the || 3)||的第一个操作数的结束 operator. 运营商。

4)The end of the first operand of the ?: conditional operator. 4)?:条件运算符的第一个操作数的结尾。

5)The end of the each operand of the comma operator. 5)逗号运算符的每个操作数的结尾。

6)Completing the evaluation of a full expression. 6)完成对完整表达的评估。 They are the following: 它们如下:

Evaluating the initializer of an auto object. 评估自动对象的初始化程序。

The expression in an 'ordinary' statement—an expression followed by semicolon. 表达式为“普通”语句 - 表达式后跟分号。

The controlling expressions in do, while, if, switch or for statements. do,while,if,switch或for语句中的控制表达式。

The other two expressions in a for statement. for语句中的另外两个表达式。

The expression in a return statement. 返回语句中的表达式。

Macros are evaluated by the preprocessor which stupidly replaces all according to the macro definitions. 宏由预处理器评估,根据宏定义愚蠢地替换所有宏。 In your case, MAX(a++, b++) becomes (a++>b++) ? a++ : b++ 在你的情况下, MAX(a++, b++)变成(a++>b++) ? a++ : b++ (a++>b++) ? a++ : b++ . (a++>b++) ? a++ : b++

If i'm correct, this is happening: 如果我是对的,就会发生这种情况:

with MAX replaced with (a>b...) you have printf("%d %d %d\\n",a,b,(a++ > b++ ? a++ : b++ ) ); 用MAX替换为(a> b ...)你有printf(“%d%d%d \\ n”,a,b,(a ++> b ++?a ++:b ++));

First, a++ > b++ is checked and both values increased (a = 4, b = 5) afterwards. 首先,检查++> b ++,之后两个值都增加(a = 4,b = 5)。 Then the second b++ gets active, but because it's postincrement, it's increased after the second value b = 5 is printed. 然后第二个b ++变为活动状态,但因为它是后增量,所以在打印第二个值b = 5之后它会增加。

Sorry for my bad english, but i hope you understand it?! 抱歉我的英语不好,但我希望你理解它?! :D :d

Greeings from Germany ;-) 来自德国的比赛;-)

Ralf 拉尔夫

So your expansion gives (adjusted for clarity): 所以你的扩展给出了(调整清晰度):

(a++ > b++) ? a++ : b++

... so (a++ > b++) is evaluated first, giving one increment each and selecting a branch based on the not-yet-incremented values of a and b . ......首先评估(a++ > b++) ,每个给出一个增量,并根据ab的尚未增加的值选择分支。 The 'else' expression is chosen, b++ , which does the second increment on b , which was already incremented in the test expression. 选择'else'表达式, b++ ,它在b上执行第二个增量,它在测试表达式中已经递增。 Since it's a post-increment, the value of b before the second increment is given to printf() . 由于它是后增量,因此第二个增量之前的b值将赋予printf()


There are two reasons for the result that you're getting here: 您到达这里的结果有两个原因:

  1. A macro is nothing but code that is expanded and pasted when you compile. 宏只是编译时扩展和粘贴的代码。 So your macro 所以你的宏

     MAX(a,b) (a>b?a:b) 

    becomes this 变成了这个

     a++>b++?a++:b++ 

    and your resulting functions is this: 你的结果是这样的:

     printf("%d %d %d\\n",a,b, a++>b++?a++:b++); 

    Now it should be clear why b is incremented twice: first in comparison, second when returning it. 现在应该清楚为什么b增加两次:首先是比较,第二次是返回。 This is not undefined behaviour, it is well defined, just analyse the code and you'll see what exactly to expect. 这不是未定义的行为,它定义明确,只需分析代码,您就会看到完全符合预期的行为。 (it is predictable in a way) (在某种程度上可以预测)

  2. The second problem here is that in C parameters are passed from last to first to the stack, hence all the incrementation will be done before you print the original values of a and b, even if they're listed first. 这里的第二个问题是,C参数从最后一个传递到第一个堆栈,因此所有增量都将在打印a和b的原始值之前完成,即使它们首先列出。 Try this line of code and you'll understand what I mean: 试试这行代码,你就明白我的意思了:

     int main(void) { int a = 3, b=4; printf("%d %d %d\\n",a,b, b++); return 0; } 

The output will be 3 5 4. I hope this explains the behaviour and will help you to predict the result. 输出将是3 5 4.我希望这可以解释行为并帮助您预测结果。
BUT , the last point depends in your compiler 但是 ,最后一点取决于您的编译器

I think the questioner was expecting the output to start: 我认为提问者期待输出开始:

3 4 ...

instead of: 代替:

4 6 ...

and this is because the parameters are being evaluated from right to left as they are being pushed onto the stack, that is, the last parameter is being evaluated first and pushed, then the second to last, then the second parameter and finally the first parameter. 这是因为参数在被推入堆栈时从右到左进行评估,也就是说,最后一个参数被评估并被推送,然后是倒数第二个参数,然后是第二个参数,最后是第一个参数。

I think (and someone post a comment if it's wrong) that this is defined in the C standard (and C++). 我认为(有人发表评论,如果它是错的),这是在C标准(和C ++)中定义的。

Update 更新

The order of evaluation is defined, but it's defined as undefined (thanks Steve). 评估顺序已定义,但定义为未定义(感谢Steve)。 Your compiler just happens to do it this way. 您的编译器碰巧就是这样做的。 I think I got confused between evaluation order and the order the parameters are passed. 我认为我在评估顺序和参数传递顺序之间感到困惑。

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

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