开发者

How to get address of va_arg?

开发者 https://www.devze.com 2022-12-27 16:29 出处:网络
I hack some old C API and i got a compile error with the following code: void OP_Exec( OP* op , ... )

I hack some old C API and i got a compile error with the following code:

void OP_Exec( OP* op , ... )
{
    int i;
    va_list vl;
    va_start(vl,op);
    for( i = 0; i < op->param_count; ++i )
    {
        switch( op->param_type[i] )
        {
            case OP_PCHAR:
                op->param_buffer[i] = va_arg(vl,char*); // ok it works
            break;
            case OP_INT:
                op->param_buffer[i] = &va_arg(vl,int); // error here
            break;
            // ... more here
        }
    }
    op->pexec(op);
    va_end(vl);
}

The error with gcc version 4.4.1 (Ubuntu 4.4.1-4ubuntu9) was:

 main.c|55|error: lvalue required as unary ‘&’ operand

So why exactly it's not possible here to get a pointer to argument?

How to fix it? This code is executed very often with different OP*, so i prefer to not allocate extra memory.

Is it possible to iterate over va_list knowing only the size of argume开发者_如何学Gonts?


Since litb's answer isn't useable for you because you can't modify the pexec() function, you could fix this using alloca() if your compiler provides it:

    switch( op->param_type[i] )
    {
        int *itmp;

        case OP_PCHAR:
            op->param_buffer[i] = va_arg(vl,char*); // ok it works
        break;

        case OP_INT:
            itmp = alloca(sizeof(int));
            *itmp = va_arg(vl, int);
            op->param_buffer[i] = itmp;
        break;
        // ... more here
    }

alloca() is usually blindingly fast, since it often is implemented using the same mechanism that is used to allocate space for local variables. The space will be automatically deallocated when the calling function exits.


Change param_buffer to be an array of

struct ValueUnion {
  Type type;
  union {
    char *stringval;
    int intval;
  } u;
};

Then you can say

op->param_buffer[i].type = op->param_type[i];
switch( op->param_type[i] )
{
    case OP_PCHAR:
        op->param_buffer[i].u.stringval = va_arg(vl,char*); // ok it works
    break;
    case OP_INT:
        op->param_buffer[i].u.intval = va_arg(vl,int); // ok it works
    break;
    // ... more here
}

You can't get the address of a variadic arg.


It won't be portable, but on some implementations, va_list is a char * to the address of the parameter on the stack. Since some platforms pass arguments in registers, and due to stack alignment issues, you don't really want to do the following:

If this is for a single platform, you could look at its stdarg.h and hack up a solution to get to the address of the parameter on the stack.

Major hack though, and not really a good idea.

0

精彩评论

暂无评论...
验证码 换一张
取 消