c variadic functions confusion

最后都变了- 提交于 2021-02-08 07:10:25

问题


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;
}

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)

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.


回答1:


The va_start, va_arg and don't forget va_end are specific for a compiler. You can not just take them from somewhere else and expect them to work. You'd better follow the manual page in using them and only try to understand their inner workings if you are a compiler engineer.

P.S: Oh, and their definitions are usually very mysterious using subtle tricks to get it working.

P.S2: In answer to your question where _builtin_va_arg is defined: it is known by the compiler, a so called builtin. You will find it in the sources of the compiler ;)




回答2:


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.

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
    }
}

Notice that neither of these _builtin_va functions can be written in C; they need to be built in to the compiler



来源:https://stackoverflow.com/questions/15712760/c-variadic-functions-confusion

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!