C - 第一个参数在可变数量的参数中的作用

时间:2017-05-18 14:44:20

标签: c

在此代码中,第一个参数值为1,表示传递的参数总数。

在被调用的函数中,我能够访问所有参数。

然后第一个参数有什么用?

不应该限制我访问多个参数。

int main(){
  fun1(1, "Apple", "Boys", "Cats", "Dogs");
  return 0;
}

void fun1(int num, ...){
   char *str;
   va_list ptr;
   va_start(ptr, num);
   str = va_arg(ptr, char *);
   str = va_arg(ptr, char *);
   printf("%s ", str);
}

输出:Boys

7 个答案:

答案 0 :(得分:6)

第一个参数的作用通常是确定可变参数的数量,有时也是类型(例如:printf())。

在x86架构上,如果使用 cdecl调用约定,则第一个参数是堆栈中最顶层的参数(具有最低地址的参数,即:在堆栈上推送的最后一个参数) 。所以你总是知道它的地址:

-------------------------------
|          1st Arg            |
-------------------------------
|        return address       | 4 bytes
-------------------------------
| saved old stack frame (EBP) | 4 bytes
------------------------------- <-- EBP

EBP + 8包含第一个参数的地址。

答案 1 :(得分:6)

对这个问题的答案有两种观点。

1。运行时的观点

va_start()va_arg()必须知道在哪里找到参数。通常,编译器将计算所有参数的固定内存位置。在编译时,这是不可能的,具有可变参数功能。 stdarg函数由运行时提供,并且知道如何在堆栈上找到参数,但它们需要一些引用。因此,您始终必须在编译期间知道一个位置:最后一个非可变参数的位置。然后va_start()va_args()可以计算下一个参数的位置。

这很可能是由于John Bollinger引用标准中至少需要一个非变量参数的实际原因,感谢这句话。

2。程序员的观点

如果您尝试读取的参数多于传递的参数,则会导致未定义的行为,因为您从没有找到参数的内存位置读取。所以你需要知道何时停止。实现此目的的一种简单方法是只传递以下参数的数量,就像在您的代码段中一样。一个众所周知的可变参数函数是printf(),它知道解析格式字符串时的参数数量。

因此,要解决您的编辑问题:没有自动限制,您可以自行决定如何让您的函数知道要阅读的参数数量,并且您只需阅读有效参数即可。

答案 2 :(得分:3)

fun1variadic。这是三个点...的含义。需要有一些方法来告诉函数它的参数列表有多长。理想情况下,还有一种方法可以告诉函数其参数的类型是什么。格式字符串printf是执行此操作的方案的示例。

不要使用可变参数函数,传递一个数组。使用可变参数函数,您几乎没有编译器支持来检测何时传递了错误的数字或类型的参数。

答案 3 :(得分:2)

如果不是第一个参数,你就不知道何时停止解析其他参数。

答案 4 :(得分:2)

要回答您的直接问题,在某些平台上需要最后一个显式参数(在您的情况下是第一个也是唯一的参数)来确定参数列表在内存中的起始位置。没有它va_start将无法始终确定指向何处。至少还有一个显式参数也可用于确定有多少可变参数。

答案 5 :(得分:2)

fun1无法知道函数调用中实际传递了多少个参数;堆栈(或寄存器)中没有任何元数据表示,&#34;是的,这块内存对应于从调用者传递的参数&#34;。

尝试将fun1称为fun1(1);(或任何其他整数值),看看会发生什么。

Variadic函数必须信任其调用者,以便在固定参数列表中为他们提供所需的信息,以正确读取任何剩余的变量参数。如果您将fun1称为fun1(0),它仍会尝试在堆栈或寄存器中找到两个char *对象,并尝试打印第二个,最有可能导致运行时错误。

fun1应该查看num参数,而不是尝试再读取任何参数。您必须信任fun1调用者才能传递与num对应的正确数字和类型

答案 6 :(得分:1)

num中的

void fun1(int num, ...){有两个目的:

  1. ...之前的参数,在这种情况下为int num,用于指示参数的变量数... - 开始并初始化{{1}的位置开始访问。 C指定

    va_list
  2. 通常 #include <stdarg.h> void fun1(int num, ...){ va_list ap; va_start(ap, num); char *s = va_arg(ap, char *); 之前的参数,以某种方式表示以下参数的数量和类型。这是未指定由C.一些典型的实现包括:

    1. ...之前的参数是以下参数的计数。这似乎与OP的预期方法很接近。

    2. 格式传递包含有关参数类型和数量的各种信息。 ...

    3. sentinel printf("%s %d\n", "Age", 25);之前的参数不表示增量计数,但最后传递的是特殊:{{1 }}

    4. 使用全局变量来指示...之后的参数数量。这种编码很少是好的做法。

  3. 我希望OP的代码是使用第一个参数来表示以下参数的计数。

    sum(1,2,3,INT_MIN);