据我所知,
cdecl
函数将其参数从右到左压入堆栈。然而,
int n = 0;
printf("%d %d", n=8, n); // Output: 8 8
我希望它输出
8 0
,因为我认为它按照从右到左推送参数的顺序来计算参数。我在 TDM-GCC 32bit/64bit
、GCC x64
、MSVC x86/x64
和 clang x64
上尝试了代码,得到了相同的结果。
所以我得出的结论是,
cdecl
函数在将参数压入堆栈之前计算其参数,并且两个过程以相反的顺序进行。
但是,
int n = 0;
printf("%d %d", n=8, n=0); // Output: 8 8
我不明白。
在 C 中,函数参数的求值顺序是未指定的。这意味着编译器可以按照它选择的任何顺序自由地计算参数。这是理解的关键点。参数压入堆栈的顺序(在 cdecl 调用约定的情况下)不一定与它们求值的顺序相同。
printf("%d %d", n=8, n);
这里,由于未指定参数的求值顺序,因此 n=8 和 n 都可以按任何顺序求值。大多数编译器从左到右评估函数参数,但 C 标准不保证这一点。在你的例子中,似乎首先评估 n=8,将 n 设置为 8。然后,当评估 n 时,它已经是 8。因此,你会看到 8 8。
printf("%d %d", n=8, n=0);
这个案例和第一个案例类似。未指定的参数求值顺序导致两个赋值都可能以任意顺序求值。如果首先计算 n=8,则 n 设置为 8,然后 n=0 可能不会更改输出,因为 printf 可能已经获取了第一个 %d 的 n 值。此行为可能因不同的编译器甚至同一编译器的不同版本而异。
C 语言中没有定义求值顺序。
您的示例打印
8 8
很可能是因为编译器已经优化了分配,并且它可以像 UB 一样执行此操作。
如果无法完成此优化,下面的示例将显示不同的行为(这取决于编译器,因为它是 UB) .
int main(void)
{
volatile int n = 0;
int m = 0;
printf("n= %d %d\n", n = 8, n);
printf("m= %d %d\n", m = 8, m);
}