在 C 函数调用中,参数传递/计算的顺序是什么?

发布于 2025-01-11 13:53:19 字数 653 浏览 0 评论 0原文

我正在运行这个 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

现在,我很困惑发生了什么。正确的顺序是什么?

I am running this C program:

#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);
}

One compiler prints this as the output:

99 99 98 98 100
99 99 98 98 100
0 2 3

Another compiler prints this:

100 100 100 99 100
100 100 100 99 99
2 3 3

Now, I am confused what is happening. What is the correct order?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

赠意 2025-01-18 13:53:19

函数调用的求值在 2018 版 C 标准条款 6.5.2.2 中指定,但该条款没有指定参数的求值顺序。任何订单都是允许的。

因此,print(++ptr, ptr--, ptr, ptr++, ++ptr); 的行为不是由 C 标准定义的。 6.5 2 说:

如果标量对象上的副作用相对于同一标量对象上的不同副作用或使用同一标量对象的值的值计算是无序的,则行为是未定义的。如果表达式的子表达式有多个允许的排序,则如果在任何排序中出现此类未排序的副作用,则行为未定义。

ptr 是一个标量对象。 (C 2018 6.2.5 21 说“算术类型和指针类型统称为标量类型。”)++ptrptr-- 更改 ptr 的副作用。 (++ptr 的“主要”作用是计算 ptr 的增量值。它的副作用是更新 ptr 存储的值code>.) 由于没有对这些副作用强加顺序,因此满足 6.5 2 的条件,因此它适用,并且 C 标准未定义该行为。

请注意,这并不仅仅意味着 ptr 可以按照编译器选择的任何顺序进行更新。当 C 标准说行为未定义时,这意味着它根本没有对行为强加任何要求。对对象的两次更新可能会发生冲突,以不同的顺序更新对象的不同字节,产生一个无法通过以任何顺序的单独连续更新获得的值。 (例如,当编译器在原始机器中使用两字节更新实现四字节指针时,可能会发生这种情况。)或者编译器中的优化器可以识别代码没有定义的行为并将其从程序中完全删除,或者将其替换为其他代码。 (例如,如果程序包含两个代码序列之间的分支,并且优化器认识到一个分支在特定情况下将具有未定义的行为,则它可以省略评估选择的代码并仅使用另一分支上的代码。 )

Evaluation of function calls is specified in the 2018 version of the C standard clause 6.5.2.2, but this clause does not specify the order of evaluation of the arguments. Any order is permitted.

As a consequence of this, the behavior of print(++ptr, ptr--, ptr, ptr++, ++ptr); is not defined by the C standard. 6.5 2 says:

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 is a scalar object. (C 2018 6.2.5 21 says “Arithmetic types and pointer types are collectively called scalar types.”) ++ptr and ptr-- change ptr by a side effect. (The “main” effect of ++ptr is to evaluate to the incremented value of ptr. Its side effect is to update the stored value of ptr.) Since there is no ordering imposed on these side effects, the conditions of 6.5 2 are satisfied, so it applies, and the behavior is not defined by the C standard.

Note that this does not just mean that ptr may be updated in whatever order the compiler happens to choose. When the C standard says behavior is undefined, that means it does not impose any requirements on the behavior at all. Two updates to the object might conflict, updating different bytes of it in different orders, producing a value that is impossible to obtain by separate consecutive updates in any order. (This could happen, for example, when a compiler implements four-byte pointers using two-byte updates in a primitive machine.) Or the optimizer in the compiler could recognize the code does not have defined behavior and remove it completely from the program or replace it by other code. (For example, if the program contains a branch between two sequences of code, and the optimizer recognizes one branch would have undefined behavior in a particular circumstances, it can omit the code that evaluates the choice and use only the code on the other branch.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文