c函数参数评估顺序

时间:2014-02-06 10:24:13

标签: c function pointers parameter-passing operator-precedence

我理解对函数的参数调用的顺序没有保证,但是,如果有一个函数调用作为参数,是不是保证会先调用该函数?

我帮助学生在实验室进行编程的入门课程,他们应该创建一个递归因子函数,它接收n(对于n!)和一个指向整数的指针,该整数将用于计算函数调用和然后他们应该打印结果(n,n!和count)。

许多人抱怨说他们使用指针是错误的,所以我查看了代码,他们都是这样的:

int fat(int n,int *count)
{
    (*count)++;
    if(n>1)
    {
        return n * fat(n-1,count);
    }

    return 1;
}

int main()
{
    int n, count=0;
    do
    {
        printf("Write n for fat (n >= 0): ");
        scanf("%d", &n);
    }while(n<0);

    printf("Input: %d. Output: %d.\nFunction called %d times.\n\n", n, fat(n, &count), count);
    printf("%d times\n",count);

    return 0;
}

使用gcc编译(Debian 4.7.2-5)4.7.2输出为:

Write n for fat (n >= 0): 4
Input: 4. Output: 24.
Function called 0 times.

4 times

因此,如果胖子应该首先运行,“函数调用...”应该打印“4次调用函数”而不是0。

所以,我的问题是:

即使保证参数中的函数调用将在“接收”之前运行,但仍然不确定它是否在查看非函数调用的参数之前运行?

另一个奇怪的事情是,这个相同的代码在xcode上打印“4次调用函数”...

5 个答案:

答案 0 :(得分:2)

  

是不是保证如果有一个函数调用作为参数,那么将首先调用该函数?

保证在以下呼叫中保证:

f(g(), h)
在调用g之前将调用

f。但是,无法保证在评估g之前调用h

通常,如果您关心首先发生两件事中的哪一件,请将它们放在单独的陈述中。这样,您就不必记住之前排序的关系,也不知道何时会出现副作用。

答案 1 :(得分:2)

在您的代码中,printf有4个参数。必须在输入printf之前评估每个参数,但不指定它们的评估顺序。编译器利用这一优势来优化代码。

C99§6.5.2.2p10:

  

功能指示符的评估顺序,实际   参数和实际参数中的子表达式是   未指定,但在实际调用之前有一个序列点。

答案 2 :(得分:1)

  

如果有一个函数调用作为参数,是不是可以保证,首先会调用该函数?

不,这不能保证。未定义实际参数的评估顺序。事实上,您的某个参数是评估函数调用的结果,不会改变任何内容。在调用函数之前,可以评估其他参数。

用你的例子:

printf("Input: %d. Output: %d.\nFunction called %d times.\n\n", n, 
    fat(n, &count), count);

printf的调用传递了4个参数。可以按编译器选择的顺序评估这些参数。

答案 3 :(得分:1)

printf("Input: %d. Output: %d.\nFunction called %d times.\n\n", n, fat(n, &count), count);

在此代码中,保证在调用printf()之前调用fat(),但无法保证nfat()和{{1}的顺序被评估 - count的值作为count的参数可以是调用printf()之前的值,或者它之后的值。此外,我认为这是完全未定义的行为,在这种情况下,fat()也可以采用绝对任何其他值。

答案 4 :(得分:1)

在C和C ++中都没有这样的保证(例外是C#)。因此,函数调用的参数可以从右到左(通常对于大多数编译器)或从左到右进行评估。函数参数的评估顺序未指定。

很明显,编译器使用它从右到左评估参数。 count首先被avaluated,因此它的值为0.然后编译器评估函数fat的调用,最后它计算n。

要获得正确的结果,您应该将printf语句拆分为两个语句

int rez = fat(n, &count); 
printf("Input: %d. Output: %d.\nFunction called %d times.\n\n", n, rez, count);