va_list с неизвестными именами типов - только размер байта известен!

У меня есть C программирование вопроса: Я хочу записать функцию со списками аргумента переменной, где определенные типы каждого аргумента не, знают - только его размер в байтах. Это означает, если я хочу получить международный Параметр, я (где-нибудь прежде) определяю: будет параметр с sizeof (интервал), который обрабатывается с функцией обратного вызова xyz.

Моя функция аргумента переменной должна теперь собрать всю информацию от своего вызова, тип данных real определенные операции (который также может быть пользовательскими типами данных), обрабатываются только через функции обратного вызова.

В стандартных va_arg-функциях не возможно сказать, "получают меня значение X байтов из моего списка параметров", таким образом, я думавший сделать это этот путь. Мой тип данных является двойным в этом случае, но это может быть любое другое число байтов (и даже переменные).

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

int fn( int anz, ... )
{
        char*   p;
        int             i;
        int             j;
        va_list args;
        char*   val;
        char*   valp;
        int             size = sizeof( double ); 

        va_start( args, anz );

        val = malloc( size );

        for( i = 0; i < anz; i++ )
        {
                memcpy( val, args, size );
                args += size;

                printf( "%lf\n", *( (double*)val ) );
        }

        va_end( args );
}

int main()
{
        fn( 1, (double)234.2 );
        fn( 3, (double)1234.567, (double)8910.111213, (double)1415.161718 );

        return 0;
}

Это работает на меня, в соответствии с Linux (gcc). Но мой вопрос теперь: это действительно портативно? Или это перестанет работать под другими системами и компиляторами?

Мой альтернативный подход должен был заменить

                memcpy( val, args, size );
                args += size;

с

            for( j = 0; j < size; j++ )
                    val[j] = va_arg( args, char );

но затем, мои значения пошли не так, как надо.

Какие-либо идеи или справка на этом?

5
задан Jan 9 July 2010 в 14:04
поделиться

6 ответов

Выполнение арифметических действий над va_list является крайним случаем непортируемости. Вы должны использовать va_arg обычно с типом того же размера, что и аргумент, и он вероятно будет работать везде. Для того, чтобы быть "ближе к переносимости", следует использовать для этой цели беззнаковые целочисленные типы (uint32_t и т.д.).

1
ответ дан 15 December 2019 в 06:13
поделиться

Ненаучный тест.

  • AIX 5.3 с GCC 4.2 - работает
  • HP-UX 11.23 с aCC 5.56 - не
  • Linux (SUSE 10.2) с GCC 4.1 - не
  • Solaris 10 с CC 5.9 - нет

Все Linux, Solaris и HP-UX жаловались на размер args +=; строка.

В противном случае совершенно очевидно, что va_arg() был включен не просто так. Например, на SPARCs стек используется совершенно по-другому.

1
ответ дан 15 December 2019 в 06:13
поделиться

Это не переносимо, извините. Формат va_list зависит от компилятора/платформы.

Вы должны использовать va_arg() для доступа к va_list, и вы должны передать правильный тип аргумента в va_list.

Однако, я полагаю, что если вы передадите va_arg тип правильного размера, то это сработает. Т.е. тип обычно не имеет значения, только размер. Однако даже это не гарантированно работает во всех системах.

Думаю, я бы предложил пересмотреть ваш дизайн и посмотреть, сможете ли вы найти альтернативный дизайн - есть ли более подробная информация о том, почему вы пытаетесь сделать это, которой вы можете поделиться? Можете ли вы вместо этого передать va_list в обратные вызовы?

Обновление

Причина, по которой байт-байтовый подход не работает, вероятно, довольно сложна. Согласно стандарту C, причина неработоспособности заключается в том, что это запрещено - вы можете использовать va_arg только для доступа к идентичным типам, которые были переданы в функцию.

Но я подозреваю, что вы хотели бы знать, что происходит за кулисами :)

Первая причина в том, что когда вы передаете "char" в функцию, он фактически автоматически преобразуется в int, поэтому сохраняется в va_arg как int. Поэтому, когда вы читаете char, вы читаете память размером в "int", а не в "char" - так что на самом деле вы не читаете байт за раз.

Еще одна причина связана с выравниванием - на некоторых архитектурах (примером могут служить последние процессоры ARM) "двойка" должна быть выровнена по границе 64 бит (а иногда даже 128 бит). То есть, для значения указателя p, p % 16 (p по модулю 16, в байтах - т.е. 128 бит) должно быть равно 0. Поэтому, когда эти значения упаковываются в va_arg, компилятор, вероятно, гарантирует, что все двойные значения имеют добавленное пространство (padding), чтобы они встречались только с правильным выравниванием - но вы не учитываете это, когда читаете записи по байту за раз.

(Могут быть и другие причины - я не очень хорошо знаком с внутренним устройством va_arg.)

.
1
ответ дан 15 December 2019 в 06:13
поделиться

В этом случае избегать va_args, вероятно, было бы чище, потому что в конечном итоге вы все равно привязываетесь к определенному количеству аргументов в коде в точке вызова. Я бы выбрал передачу массивов аргументов.

  struct arg
  {
     void* vptr;
     size_t len;
  };

  void fn( struct arg* args, int nargs );

Если уж на то пошло, я бы также передал определение данных, либо int, как было сказано ранее в комментариях, либо указатель на struct, если речь идет о более сложных данных def.

.
0
ответ дан 15 December 2019 в 06:13
поделиться

Могу я предложить заменить аргументы переменных массивом пустых указателей?

0
ответ дан 15 December 2019 в 06:13
поделиться

Для C99 , if Я могу предположить, что все ваши аргументы относятся к целочисленным типам, но могут иметь разную ширину или знак, вы можете обойтись макросом. Таким образом, вы даже можете преобразовать свой список аргументов в массив безболезненно для пользователя, который это вызывает.

#define myFunc(...) myRealFunc(NARGS(__VA_ARGS__), (uintmax_t const[]){ __VA_ARGS__})

где

void myRealFunc(size_t len, uintmax_t const param*);

и где NARGS - макрос, который дает вам длину параметра __ VA_ARGS __ . Такая вещь затем может быть вызвана точно так же, как функция, которая будет получать va_list .

Чтобы немного объяснить, что делает макрос:

  • он помещает количество аргументов в первый параметр myRealFunc
  • , он создает временный массив ( составной литерал ) правильный размер и инициализирует его аргументами (все приводятся к uintmax_t )
  • , если NARGS выполняется правильно, это оценивает ваш список аргументов только в время предварительной обработки, как токены, и не во время выполнения. Итак, ваши параметры не оцениваются во время выполнения более чем Once

Теперь ваши функции обратного вызова будут вызываться myRealFunc с использованием любой магии, которую вы хотите поместить туда.Поскольку при вызове им потребуется параметр другого целочисленного типа, параметр uintmax_t параметр param [i] будет возвращен в этот тип.

0
ответ дан 15 December 2019 в 06:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: