Как получить адрес va_arg? - PullRequest
1 голос
/ 20 апреля 2010

Я взломал старый C API и получил ошибку компиляции со следующим кодом:

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

Ошибка с gcc version 4.4.1 (Ubuntu 4.4.1-4ubuntu9) было:

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

Так почему же здесь невозможно получить указатель на аргумент?

Как это исправить? Этот код выполняется очень часто с разными значениями OP*, поэтому я предпочитаю не выделять дополнительную память.

Можно ли перебирать va_list, зная только размер аргументов?

Ответы [ 3 ]

2 голосов
/ 20 апреля 2010

Так как ответ litb для вас непригоден, поскольку вы не можете изменить функцию pexec(), вы можете исправить это, используя alloca(), если ваш компилятор предоставляет:

    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() обычно очень быстр, поскольку он часто реализуется с использованием того же механизма, который используется для выделения места для локальных переменных. Пространство будет автоматически освобождено при выходе из вызывающей функции.

2 голосов
/ 20 апреля 2010

Измените param_buffer на массив

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

Тогда вы можете сказать

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
}

Вы не можете получить адрес переменной аргументации.

0 голосов
/ 20 апреля 2010

Он не будет переносимым, но в некоторых реализациях va_list является char * адресом параметра в стеке. Так как некоторые платформы передают аргументы в регистрах, и из-за проблем с выравниванием стека вы действительно не хотите делать следующее:

Если это для одной платформы, вы можете посмотреть на ее stdarg.h и найти решение, чтобы добраться до адреса параметра в стеке.

Главный взлом, хотя и не очень хорошая идея.

...