变异函数混乱 [英] c variadic functions confusion

查看:66
本文介绍了变异函数混乱的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试找出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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆