变异函数混乱 [英] c variadic functions confusion
问题描述
我正在尝试找出va_start()和va_arg()宏背后的含义.下面的代码运行良好.
I'm trying to figure out what's behind va_start(), va_arg() macroses. The code below works well.
#include <iostream>
#include <cstdarg>
void f(double a, double b, ...)
{
va_list arg;
va_start(arg, b);
double d;
while((d = va_arg(arg, double)) != 0)
{
std::cout << d << '\n';
}
}
int main(int argc, char *argv[])
{
f(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 0.0);
return 0;
}
正如我期望的那样,它给出了这样的输出:3 4 5 6 7 8 9.然后我发现了这些宏的定义(在互联网上,因为我的标头stdarg.h很神秘-它像_builtin_va_arg(v,l)一样定义了宏va_arg(v,l),最后一个未在其中定义并且stdarg.h不包含任何内容,因此在某些库中也是如此??).不过,我写了"cstdarg"代替了:
As I was expected it gave such output: 3 4 5 6 7 8 9. Then I found definitions of that macroses (in internet, cause my header stdarg.h is mysterious -- it defines macros va_arg(v, l) like _builtin_va_arg(v, l), the last doesn't defined in it and stdarg.h doesn't include anything, so is it in some library??? ). Nevertheless, in place of "cstdarg" I wrote:
typedef char* va_list;
#define _INTSIZEOF(n) \
((sizeof(n)+sizeof(int)-1) &~(sizeof(int)-1))
#define va_start(ap,v) \
(ap = (va_list)&v + _INTSIZEOF(v))
#define va_arg(ap,t) \
(*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))
#define va_end(ap) (ap = (va_list)0)
输出变得奇怪,例如1-0.0409377-0.04093774.88084e-2704.85706e-2701个2个3456789.我以为可变参数被放置在最后声明的参数的旁边,但是显然情况更加复杂.如果有人揭露我错了地方或那里到底发生了什么,我会感到非常高兴.
The output became wierd, such as 1 -0.0409377 -0.0409377 4.88084e-270 4.85706e-270 1 2 3 4 5 6 7 8 9. I thought that variadic parameters are placed just next to last declared parameter, but apparently there is more intricated situation. I'd be very pleased if somebody unveil where am I wrong or what's really happens there.
推荐答案
您的手写宏将在始终传递堆栈上所有参数并将较小类型填充到sizeof(int)的计算机上工作.但是,许多机器(最近的日子?)没有在堆栈上传递参数-而是在寄存器中传递参数,并且仅在寄存器中容纳太多参数时才使用堆栈.
Your handwritten macros would work on a machine that always passes all arguments on the stack and pads up smaller types to sizeof(int). However, many machines (most these days?) don't pass arguments on the stack -- they instead pass them in registers and only use the stack if there are too many to fit in registers.
因此,为了处理va_args,编译器需要知道ABI,以及在什么情况下将哪些参数放在何处.通常要做的是让va_list包含多个数组(足以容纳可能包含args的所有寄存器)和多个指针(通常,每种类型的寄存器一个,而堆栈的指针).va_start转储所有参数注册到数组中并初始化指针,然后va_arg找出给定参数类型将传入哪种寄存器,并将值拉出适当的位置,因此对于一个具有8个reg的整数/指针args的假设处理器和对于float/double args的8条规则,您可能会遇到类似这样的情况:
So in order to deal with va_args, the compiler needs to know the ABI and which arguments will be placed where in what circumstances. What is generally done is to have a va_list contain a number of arrays (enough to hold all the registers that might contain args) and a number of pointers (generally, one for each type of register and one for the stack. va_start dumps all the argument registers into the arrays and initializes the pointers, and va_arg then figures out what kind of register the given argument type would be passed in and pulls the value out of appropriate spot. So for a hypothetical processor with 8 regs for integer/pointer args and 8 regs for float/double args, you might have something like:
typedef struct {
intptr_t iregs[8], *iptr;
double fregs[8], *fptr;
char *spptr;
} va_list;
inline void _builtin_va_start(va_list &ap, arg) {
// dump the registers might be used to pass args into ap->iregs and ap-fregs,
// setup iptr and fptr to point into iregs and fregs after the arguments that
// correspond to 'arg' and those before it. spptr points into the stack space
// used for arguments after regs run out
}
inline _builtin_va_arg(va_list &ap, type) {
if (type is integer or pointer) {
if (ap->iptr == ap->iregs+8) {
rv = *(type *)ap->spptr;
ap->spptr += sizeof(type);
} else {
rv = *ap->iptr++;
}
} else if (type is float or double) {
if (ap->fptr == ap->fregs+8) {
rv = *(type *)ap->spptr;
ap->spptr += sizeof(type);
} else {
rv = *ap->fptr++;
}
} else {
// some other type (struct?) deal with it
}
}
请注意,这两个 _builtin_va
函数均不能用C编写;它们需要内置到编译器中
Notice that neither of these _builtin_va
functions can be written in C; they need to be built in to the compiler
这篇关于变异函数混乱的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!