具有无限多个(未命名)参数的函数-如何正确访问这些参数?

时间:2018-12-01 09:00:12

标签: c

我有一个很棘手的问题,我今天一直在玩,但是我还不能提出一个优雅的解决方案。

我们知道,void func()形式的函数接受无限多个参数。现在,我整理了一个非常简单的最小工作代码段:

#include <stdio.h>

int func()
{

    printf("%d, %d, %d, %d\n");
    return 0;
}

int func() {
    sucks(0 /* Offset */, 1, 2, 3, 4);
}

好的,我们现在可以根据需要调用任意数量的func()了。我正在试验atm的问题是:我们如何才能正确访问这些参数的内容? printf()函数打印出类似这样的内容……只是为了向我们自己验证参数是否确实存在:

anonymous@melina:/tmp$ ./a.out 
1 2 3 4

所以现在是一个问题:上面的代码片段有些黑。是否有访问这些参数的正确方法?或者,您是否真的需要弄乱堆栈指针和内联汇编?第一次尝试时,我曾想过要在函数的开头获取堆栈指针,例如

uint64_t sp;
asm( "mov %%rsp, %0" : "=rm" ( sp ));

...并以某种方式使用它来猜测这些参数实际上在内存中的位置。但是……到目前为止我还没有成功。

2 个答案:

答案 0 :(得分:6)

  

是否有访问这些参数的正确方法?

是的。在函数定义中指定参数列表以及函数的类型和标识符。

您可以按“旧样式”进行操作(不要这样做,不应在新代码中使用它):

int func(a, b, c, d)
    int a; 
    int b;
    int c;
    int d;
{
   printf("%d %d %d %d\n", a, b, c, d);
}

或正常:

int func(int a, int b, int c, int d) {
   printf("%d %d %d %d\n", a, b, c, d);
}

或使用stdarg.h:

int func(int a, ...) {
   va_list ap; 
   va_start(va, a); 
   int b = va_arg(va, int);
   int c = va_arg(va, int);
   int d = va_arg(va, int);
   printf("%d %d %d %d\n", a, b, c, d);
   va_end(ap);
}

或使用第二个arg中的stdarg.h:

int func(int a, int b, ...) {
   va_list ap; 
   va_start(va, b); 
   int c = va_arg(va, int);
   int d = va_arg(va, int);
   printf("%d %d %d %d\n", a, b, c, d);
   va_end(ap);
}

或使用第三个arg中的stdarg.h:

int func(int a, int b, int c, ...) {
   va_list ap; 
   va_start(va, c); 
   int d = va_arg(va, int);
   printf("%d %d %d %d\n", a, b, c, d);
   va_end(ap);
}

stdarg.h至少需要声明参数列表中的第一个参数。因此,没有办法处理带有未指定数量和参数类型的函数,但是有一种方法(stdarg.h)处理带有至少1个参数后跟未指定数量和参数类型的函数。

  

...形式为void func()的函数接受无限多个参数

这不是事实。我认为没有C标准的方法可以将无数个参数传递给函数。

格式为func()的函数采用 unspecified 的数量和类型的参数。它们采用有限数量的参数,但是在遇到此类函数声明的事务单元中未指定数量。如果函数定义带有5个参数,则它们带有5个参数,否则为未定义的行为。如果函数定义采用省略号(,...)(在这种情况下为no information about the number or types of the parameters after the comma is supplied.C11 6.7.6.3p9),则不指定参数的数量。

  

还是您必须真正弄乱堆栈指针和内联汇编?

是的,您必须“困惑”于实现定义的行为。

  

来自GCC docs 18.10

  

... va_start的ISO实现带有另一个第二个参数。用户应该在此处编写该函数的最后一个命名参数。

     

但是,va_start不应使用此参数。

您需要为va_start指定第二个参数。也许可以在this thread中找到有关该主题的更多信息。

  

printf(“%d%d%d%d \ n”);

这是未定义的行为。程序具有未定义的行为后,请nasal demons start spawning in your room and are fighting for independence。它可能在您的平台或实现上具有明确定义的行为,但这是错误的,它不再是C语言。 this thread中的If there is a "correct" behavior according to your mental model of the language, that model is simply wrong;。从语言的角度来看,鼻恶魔为独立而战,该程序无效。

答案 1 :(得分:1)

  

[...]形式为void func()的函数接受无限多个参数。

不。

必须在没有原型的情况下使用功能

void foo(); // not a prototype
int main(void) {
    // foo() must be called according to its definition
    foo(12, 4, "bar", -42); // error (undetected by compiler)
    foo("---", 12, 4, "bar", -42); // no error
}
void foo(const char *msg, int n, ...) { /* ... */ }

请注意,C89之前的函数定义具有不同的参数定义方式。向后兼容性的愿望仍然允许这种语法。我相信上述限制同样适用

void bar() /* pre-C89 syntax */
const char *msg;
int n;
{ /* ... */ }