在 C 函数调用中,参数 passed/evaluated 的顺序是什么?
In a C function call, in which order are the parameters passed/evaluated?
我是运行这个C程序:
#include<stdio.h>
void print(int* a, int* b, int* c, int* d, int* e)
{
printf("%d %d %d %d %d\n", *a, *b, *c, *d, *e);
}
int main()
{
static int a[] = {97, 98, 99, 100, 101, 102};
int* ptr = a + 1;
print(++ptr, ptr--, ptr, ptr++, ++ptr);
ptr = a+ 1;
printf("%d %d %d %d %d\n", *++ptr, *ptr--, *ptr, *ptr++, *++ptr);
int i = 0;
printf("%d %d %d", i++, ++i, ++i);
}
一个编译器将其打印为输出:
99 99 98 98 100
99 99 98 98 100
0 2 3
另一个编译器打印如下:
100 100 100 99 100
100 100 100 99 99
2 3 3
现在,我对发生的事情感到困惑。正确的顺序是什么?
函数调用的求值在2018版C标准条款6.5.2.2中有所规定,但该条款并未规定参数的求值顺序。允许任何顺序。
因此,print(++ptr, ptr--, ptr, ptr++, ++ptr);
的行为未由 C 标准定义。 6.5 2 说:
If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined. If there are multiple allowable orderings of the subexpressions of an expression, the behavior is undefined if such an unsequenced side effect occurs in any of the orderings.
ptr
是标量对象。 (C 2018 6.2.5 21 说“算术类型和指针类型统称为标量类型。”)++ptr
和 ptr--
通过副作用改变 ptr
。 (++ptr
的“主要”作用是评估 ptr
的增量值。它的副作用是更新 ptr
的存储值。)因为没有强加于这些副作用,满足6.5 2的条件,所以适用,C标准没有定义行为。
请注意,这不仅仅意味着 ptr
可以按照编译器碰巧选择的任何顺序进行更新。当 C 标准说行为未定义时,这意味着它根本不对行为强加任何要求。对该对象的两次更新可能会发生冲突,以不同的顺序更新它的不同字节,从而产生一个不可能通过以任何顺序单独连续更新获得的值。 (例如,当编译器在原始机器中使用 two-byte 更新实现 four-byte 指针时,可能会发生这种情况。)或者编译器中的优化器可以识别代码没有定义的行为并将其完全删除从程序或用其他代码替换它。 (例如,如果程序包含两个代码序列之间的分支,并且优化器识别出一个分支在特定情况下会有未定义的行为,它可以省略评估选择的代码并仅使用另一个分支上的代码。 )
我是运行这个C程序:
#include<stdio.h>
void print(int* a, int* b, int* c, int* d, int* e)
{
printf("%d %d %d %d %d\n", *a, *b, *c, *d, *e);
}
int main()
{
static int a[] = {97, 98, 99, 100, 101, 102};
int* ptr = a + 1;
print(++ptr, ptr--, ptr, ptr++, ++ptr);
ptr = a+ 1;
printf("%d %d %d %d %d\n", *++ptr, *ptr--, *ptr, *ptr++, *++ptr);
int i = 0;
printf("%d %d %d", i++, ++i, ++i);
}
一个编译器将其打印为输出:
99 99 98 98 100
99 99 98 98 100
0 2 3
另一个编译器打印如下:
100 100 100 99 100
100 100 100 99 99
2 3 3
现在,我对发生的事情感到困惑。正确的顺序是什么?
函数调用的求值在2018版C标准条款6.5.2.2中有所规定,但该条款并未规定参数的求值顺序。允许任何顺序。
因此,print(++ptr, ptr--, ptr, ptr++, ++ptr);
的行为未由 C 标准定义。 6.5 2 说:
If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined. If there are multiple allowable orderings of the subexpressions of an expression, the behavior is undefined if such an unsequenced side effect occurs in any of the orderings.
ptr
是标量对象。 (C 2018 6.2.5 21 说“算术类型和指针类型统称为标量类型。”)++ptr
和 ptr--
通过副作用改变 ptr
。 (++ptr
的“主要”作用是评估 ptr
的增量值。它的副作用是更新 ptr
的存储值。)因为没有强加于这些副作用,满足6.5 2的条件,所以适用,C标准没有定义行为。
请注意,这不仅仅意味着 ptr
可以按照编译器碰巧选择的任何顺序进行更新。当 C 标准说行为未定义时,这意味着它根本不对行为强加任何要求。对该对象的两次更新可能会发生冲突,以不同的顺序更新它的不同字节,从而产生一个不可能通过以任何顺序单独连续更新获得的值。 (例如,当编译器在原始机器中使用 two-byte 更新实现 four-byte 指针时,可能会发生这种情况。)或者编译器中的优化器可以识别代码没有定义的行为并将其完全删除从程序或用其他代码替换它。 (例如,如果程序包含两个代码序列之间的分支,并且优化器识别出一个分支在特定情况下会有未定义的行为,它可以省略评估选择的代码并仅使用另一个分支上的代码。 )