【问题标题】:How are arguments evaluated in a function call? [duplicate]如何在函数调用中评估参数? [复制]
【发布时间】:2010-03-10 21:20:24
【问题描述】:

考虑这段代码:

void res(int a,int n)
{
    printf("%d %d, ",a,n); 
}

void main(void) 
{
    int i; 
    for(i=0;i<5;i++)
        res(i++,i);
    //prints 0 1, 2 3, 4 5

    for(i=0;i<5;i++)
        res(i,i++);
    //prints 1 0, 3 2, 5 4
}

查看输出,似乎不是每次都从右到左评估参数。这里到底发生了什么?

【问题讨论】:

    标签: c


    【解决方案1】:

    函数调用中参数的求值顺序未指定。编译器可以按照它可能决定的任何顺序对它们进行评估。

    来自 C99 标准 6.5.2.2/10“函数调用/语义”:

    函数指示符的求值顺序、实际参数和 实际参数中的子表达式未指定,但有一个序列点 在实际通话之前。

    如果您需要确保特定的排序,通常使用临时解决方法:

    int i; 
    for(i=0;i<5;i++) {
        int tmp = i;
        int tmp2 = i++;
    
        res(tmp2,tmp);
    }
    

    更重要的是(因为它会导致未定义的行为,而不仅仅是未指定的行为)是您通常不能在表达式中多次使用递增/递减运算符的操作数。那是因为:

    在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的评估修改一次。此外,应仅读取先验值以确定要存储的值。 (6.5/2“表达”)

    【讨论】:

      【解决方案2】:

      根据标准:参数评估的顺序未指定。此外,请注意,在评估参数时没有序列点(类似于里程数)。因此,修改同一个变量作为参数的一部分,不止一次会引发未定义的行为。这是FAQ 3.2。因此,您发布的代码具有模棱两可的行为。

      对于标准为什么未指定它可能会令人惊讶:简单的原因是这允许编译器执行一些优化。 (请参阅与GOTW #56 的 Q2 相关的讨论。)

      不过,在大多数实现中,这取决于所谓的调用约定。调用约定不仅决定了顺序,而且将清理堆栈的责任强加给调用者或被调用者。

      另请注意,main 始终返回 int

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-06-21
        • 1970-01-01
        • 2011-12-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-02-15
        相关资源
        最近更新 更多