C 中函数参数的调用约定和求值顺序之间的区别?

问题描述 投票:0回答:1

以下代码在不同编译器上产生不同的输出;

#include <stdio.h>

void fun(int x, int y, int z)  /* function definition */
{
    printf("%d %d %d \n", x, y, z);  /* 6 6 6 */
}

int main()
{
    int a = 3;
    fun(++a, ++a, ++a);  /* function call */
    return 0;
}

到目前为止,代码已经产生了输出

  • 6 6 6
  • 6 5 4
  • 4 5 6

在不同的编译器上。

此行为是否与函数参数的求值顺序有关,或者可能是由于调用约定(cdecl)所致?更改约定(如果可能的话)会以任何方式改变上述代码的输出吗?

代码片段来自一本试图介绍 C 语言调用约定的书。我是 C 新手,所以请使其尽可能简单或提供上下文。

c function calling-convention
1个回答
0
投票

调用约定指定在调用函数时和函数返回时计算机必须处于的状态。它没有说明计算机如何到达该状态,因此它也没有说明必须评估函数参数的顺序。

例如,调用约定可能会规定,对于特定的函数接口,参数 1、2、3 和 4 必须分别位于处理器寄存器 A、处理器寄存器 B、堆栈上的位置 X 和堆栈上的位置 Y堆栈。然而,调用例程可以计算参数 2 并将其放入寄存器 B,然后计算参数 3 并将其放入位置 X,然后计算参数 4 并将其放入位置 Y,然后计算参数 1 并将其放入寄存器 A。只要在调用函数时将所有参数放入其所需的位置,就满足调用约定。

fun(++a, ++a, ++a)
中,调用约定和 C 标准都没有指定参数求值的顺序。编译器可以自由生成以任何顺序评估参数的指令。此外,预自增运算符由两个操作组成:递增操作数的值和使用操作数的值。这些操作没有捆绑在一起,因此
a
fun(++a, ++a, ++a)
的值的三个增量和三个用途可以重新排列成任何顺序。

C 标准规定,当对标量对象的多次修改未排序或对标量对象的修改未按其值的使用进行排序时,程序的行为未定义。因此,执行

fun(++a, ++a, ++a)
的程序的行为不是由 C 标准定义的。

© www.soinside.com 2019 - 2024. All rights reserved.