C语言变参函数,可变参数是如何入栈的?

viod func(const char *fmt, ...)
{
    printf("%p", &fmt);
}

函数调用:

func("test",1,2,3,4,5,6,7);

这段代码我用GDB查看内存,发现fmt这个参数临近的内存都没有1、2、3、4、5、6、7这些数字出现。
那么这些参数都去哪里了呢?程序是否单独分配了空间(栈外)存储这些值?如果是的话,该如何找到这个空间呢?

使用gcc-9.1.0编译
系统版本:CentOS Linux release 7.3.1611 (Core)


鉴于有人可能会推荐我使用stdarg.h里的va_listva_startva_argva_end宏。在此我先谢过。
我的目的是写出一个不需要stdarg.h头文件的变参函数!

阅读 3.7k
1 个回答

这个参数咋传,跟你用得编译器、操作系统,目标体系结构都有关 ... ,可以去看一下Calling Convetion简单了解一下。

对 gcc Linux x86-64 来说,开始的几个参数根本不在栈里,而是用的寄存器。

把你的函数拼了个程序,放到godbolt上看了一下:

void func(const char *fmt, ...)
{
    printf("%p", &fmt);
}

int main(int argc, const char **argv) {
    func("test", 1, 2, 3, 4, 5, 6, 7);
}

func 的调用是这样:

        push    7
        push    6
        mov     r9d, 5
        mov     r8d, 4
        mov     ecx, 3
        mov     edx, 2
        mov     esi, 1
        mov     edi, OFFSET FLAT:.LC1
        mov     eax, 0
        call    func
        add     rsp, 16
        mov     eax, 0

可以看出除了 6 跟 7 ,其他所有参数都在寄存器里。

进入 func 之后,这些参数又被从寄存器放回了内存(为了实现 va_list 等等 ...):

        push    rbp
        mov     rbp, rsp
        sub     rsp, 192
        mov     QWORD PTR [rbp-184], rdi
        mov     QWORD PTR [rbp-168], rsi
        mov     QWORD PTR [rbp-160], rdx
        mov     QWORD PTR [rbp-152], rcx
        mov     QWORD PTR [rbp-144], r8
        mov     QWORD PTR [rbp-136], r9

然而第一个参数跟后续的并不连续,并且参数 5 (后放进内存的)跟 参数 6 (直接压栈)也并不连续。

最后,其他的编译器/操作系统/体系结构的组合,可能完全不是这么搞的。比如 x86(32位) ,所有参数都会被压倒栈里。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题