我正在学习 C,所以我尝试了下面的代码并得到输出7,6
而不是6,7
. 为什么?
#include <stdio.h>
int f1(int);
void main()
{
int b = 5;
printf("%d,%d", f1(b), f1(b));
}
int f1(int b)
{
static int n = 5;
n++;
return n;
}
我正在学习 C,所以我尝试了下面的代码并得到输出7,6
而不是6,7
. 为什么?
#include <stdio.h>
int f1(int);
void main()
{
int b = 5;
printf("%d,%d", f1(b), f1(b));
}
int f1(int b)
{
static int n = 5;
n++;
return n;
}
函数参数的计算顺序在 C 中未指定。(注意这里没有未定义的行为;例如,不允许同时计算参数。)
通常,参数的评估是从右到左,或者从左到右。
根据经验,如果该函数具有副作用(如您的情况),或者如果您两次传递相同的参数以允许调用站点中的某些内容,则不要在函数参数列表中调用相同的函数两次被修改(例如传递一个指针)。
https://en.cppreference.com/w/c/language/eval_order
在 C11 之前,您必须遵守规则 (2)
There is a sequence point after evaluation of the first (left) operand and
before evaluation of the second (right) operand of the following binary
operators: && (logical AND), || (logical OR), and , (comma).
因为参数在 C11 之前被认为是用逗号分隔的。这不是最优的,因为参数在某些平台上是从右向左推送的。因此,C11 添加了规则 (12) 使其未指定。
A function call that is not sequenced before or sequenced after another
function call is indeterminately sequenced (CPU instructions that
constitute different function calls cannot be interleaved, even if the
functions are inlined)
即使是 C99 指定的初始值设定项,仍然会回到规则 (2),其中较早的(左)初始值设定项在与逗号运算符相关的较晚(右)初始值设定项之前被解析。也就是说,直到 C11 添加规则 (13) 使其未指定。
In initialization list expressions, all evaluations are indeterminately
sequenced
换句话说,在规则 (12) 和规则 (13) 之前,规则 (2) 中的逗号运算符是指定的行为。规则 (2) 导致无法在某些平台上优化的低效代码。如果结构成员或函数参数的数量超过某个阈值,则没有足够的寄存器。也就是说,“注册压力”成为一个问题。
从历史上看,聚合类型初始值设定项和函数参数回退到逗号运算符。在 C11 中,他们特别添加了在这些聚合类型初始化程序和函数参数中的逗号不是“逗号运算符”的定义,因此规则 (12) 和规则 (13) 有意义,并且规则 (2) 不适用。