可变参数函数调用的工作原理是什么?

4

我正试图理解变参函数的工作原理。我读了man stdarg并编写了以下代码:

#include <stdarg.h>
#include <stdio.h>

int sum(int count, ...){
    va_list lst;
    va_start(lst, count);
    printf("First=%i,Second=%i,Third=%i, Fourth=%i, Fifth=%i\n",va_arg(lst,int),va_arg(lst,int),va_arg(lst,int),va_arg(lst,int),va_arg(lst,int));
}

int main(){
    sum(1,2,3,4);
}

编译和运行后,我得到了以下输入:

First=0,Second=134513840,Third=4,Fourth=3,Fifth=2

我不理解这个。我期望的是First=2,Second=3,Third=4,而Fourth/Fifth的值未定义,因为在函数调用之后,参数从右向左推入堆栈,va_arg(lst, int)只返回一个指向深层堆栈元素的指针。


3
你“说谎”关于参数的数量。 - Matthias
6
函数(printf)的参数没有被定义的求值顺序(没有序列点),因此您各自的va_arg是以无特定顺序进行求值的,而不是从左到右。您应该首先将每个va_arg存储在变量(或数组)中,然后在printf调用中使用它们。 - Bernd Elkemann
我认为大多数实现 tend to 从右到左评估参数。尝试 #include <stdio.h> int main() {int n=1;printf("%d %d %d\n",n++,n++,n++);}。它很可能会打印出 3 2 1 - halfbit
然而,未定义行为是未定义行为,不能依赖它。 - tangrs
2个回答

4

有一些小错误(第一个是我在评论中提示的):

  • 函数参数(这里是printf()函数的参数)会按任意顺序进行评估(不一定是从左到右)。您需要通过首先将va_arg调用存储在变量或数组中来添加程序的序列点。
  • 在第一个参数中,您承诺提供args的数量count,但未给出。
  • 函数承诺返回int

完整代码示例如下:

#include <stdarg.h>
#include <stdio.h>
int sum(int count, ...){
    if(count!=5) { printf("this version expects 5 variable args"); return 1; }
    va_list lst;
    va_start(lst, count);
    int a1 = va_arg(lst,int);
    int a2 = va_arg(lst,int);
    int a3 = va_arg(lst,int);
    int a4 = va_arg(lst,int);
    int a5 = va_arg(lst,int);
    va_end(lst); // added for cleanup
    printf("First=%i,Second=%i,Third=%i, Fourth=%i, Fifth=%i\n", 
         a1, a2, a3, a4, a5);
    return 0;
}
int main(){
    sum(5, 1,2,3,4,5);
    return 0;
}
    // prints: First=1,Second=2,Third=3, Fourth=4, Fifth=5

请注意,您的程序期望可变参数的数量为5,但可变参数函数通常是要求参数数量可变的,您可以这样编写代码:

请注意,您的程序期望可变参数的数量为5,但可变参数函数通常是要求参数数量可变的,您可以这样编写:

#include <stdarg.h>
#include <stdio.h>
int sum(int count, ...){
    va_list lst;
    va_start(lst, count);
    int i=0; for(; i<count; ++i) {
        printf("at %i is %i\n", i, va_arg(lst, int));
    }
    va_end(lst);
    return 0;
}
int main(){
    sum(5, 1,2,3,4,5);
    return 0;
}

0

这应该会产生所期望的解决方案。

#include <stdarg.h>
#include <stdio.h>

int sum(int count, ...){
    va_list lst;
    va_start(lst, count);
    int first=va_arg(lst,int),second=va_arg(lst,int),third=va_arg(lst,int),fourth=va_arg(lst,int),fifth=va_arg(lst,int);
    printf("First=%i,Second=%i,Third=%i, Fourth=%i, Fifth=%i\n",first,second,third,fourth,fifth);
}

int main(){
    sum(5,1,2,3,4,5);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接