如何实现可变长度参数列表?

Pau*_*nta 5 c implementation variadic-functions

当调用使用varargs的函数时,内部会发生什么?参数本身是否像任何其他参数一样存储在堆上或堆栈中.如果在堆栈中,它是如何工作的?

Oli*_*rth 8

它依赖于实现.但最有可能的是,args被一个接一个地放在堆栈上(在执行默认参数提升之后).

va_start,va_arg等等.只是通过堆栈走了指针,重新诠释位作为你问任何类型的工作.


cha*_*ite 5

就像之前提到的那样,它依赖于实现.

在C调用约定(称为cdecl)中,参数以相反的顺序被推入堆栈,因此:

void myfunc(int one, int two, int three)
Run Code Online (Sandbox Code Playgroud)

在调用之后堆栈看起来会像这样(堆栈向上增长,朝向0):

  .                .                0x00000000
  .                .
  .                .
  | current frame  |
  |----------------|
  | return address |
  |----------------|                   ^
  |    one         |                   | stack
  |----------------|                   | growth
  |    two         |                   | direction
  |----------------|                   |
  |    three       |
  |----------------|
  | previous frame |
         ...       
         ...                        0xFFFFFFFF
Run Code Online (Sandbox Code Playgroud)

因此,可以首先获取第一个参数(因为我们知道它的位置,它就在返回地址之前),并且希望它包含有关存在多少其他参数的足够信息.例如,在printf(3)和相关函数中,有关其他参数的所有信息都存在于格式字符串中,这是第一个参数.

  • 图中"向上",但数字表示"向下". (2认同)

Ese*_*gün 1

在 C 中,函数参数都由调用者函数压入堆栈或从堆栈中拉出。调用者函数知道有多少项被推送,因此它也能够在调用后将它们拉回。被调用者只能从其他参数中推断出参数的数量,例如格式字符串printf()。

例如,在 Pascal 中,堆栈上的参数由被调用者提取。由于被调用者不知道压入的项目数,因此它也无法将堆栈恢复到之前的状态。这就是为什么在 Pascal 中不可能实现可变参数。