如何在C中实现stdarg

时间:2015-08-29 23:49:06

标签: c header-files

为了好奇,我希望为标准C库中的某些功能编写最小的替换。到目前为止,我已完成printf()strlen()strcpy()memcpy()memset()等...但是当我尝试使用printf函数时,我不知道如何实施stdarg.h!我能用这种方式做什么?

是使用宏还是实际功能?

我在32位x86上使用gcc OR clang,如果它有助于使这更容易回答。

4 个答案:

答案 0 :(得分:4)

在带有cdecl调用约定的32位x86上,参数在栈上传递:

^ higher addresses (lower on the stack)
|
| caller local variables
| ...
| argument 3
| argument 2
| argument 1
| return address
| saved EBP (usually)
| callee local variables
|
v lower addresses (higher on the stack)

您可以将va_list实现为指针。 va_start可以获取传递给它的参数的地址,并添加该参数的大小以移动到下一个参数。 va_arg可以访问指针并将其添加到下一个参数。 va_copy只能复制指针值。 va_end不需要做任何事情。

另一方面,如果你没有使用cdecl(也许你正在使用fastcall),你不是32位,你不是x86,这是行不通的;您可能需要使用寄存器而不仅仅是指针值。即使它仍然不能保证工作,因为你依赖于未定义的行为;作为一个潜在问题的例子,内联可能会破坏一切。这就是为什么头文件只是typedef到内置的编译器 - 在C中实现它是没有希望的,你需要编译器支持。甚至不让我开始实施setjmplongjmp ...

答案 1 :(得分:3)

无法在C中实现stdarg.h宏;你需要像__builtin_va_arg这样的编译器内置函数,GCC和兼容的编译器提供的,或者编译器的等价物。

即使你知道你正在使用的特定目标的传递约定(如icktoofay的回答中的i386),C中也无法访问这个内存。简单地对传递给va_start的地址执行指针算法是无效的;它导致未定义的行为。但即使C确实允许该算术,也不能保证最后一个命名参数的地址实际上对应于它作为调用约定的一部分在栈上传递的位置;编译器可以选择将其移动到其堆栈帧中的不同位置(可能是为了获得额外的对齐或数据位置)。

答案 2 :(得分:2)

您可以看到如何实现va宏here的示例。此标头用于VC ++,每个处理器体系结构都有不同的实现。这些宏似乎并不特定于Microsoft编译器。在GCC和Clang中,va宏指的是编译器内置函数。

答案 3 :(得分:2)

1992年发布到comp.sources.unix的CALC源代码中有一个实现。

这是来自shar存档,因此请忽略$scope.branchCode = "xyz branch code" s。

X