va_arg выдает мусорный текст - PullRequest
3 голосов
/ 15 ноября 2011

Я сделал простой тестовый пример:

static void va_test(char* str_arg, ...)
{ 
  va_list ap;
  va_start(ap, str_arg); 
  for( ; ; ) { 
    if (str_arg == NULL)
      break; 
    int n = va_arg(ap,int);
    printf("arg: %s,%d\n", str_arg, n);
    str_arg = va_arg(ap,char*);
  }
  va_end(ap);
  printf("\n");
}

Когда я запускаю его с va_test("beer",1,"cofe",2,"juice",3,0) в автономном исполняемом файле, он работает нормально.Но когда я вызываю его из исполняемого файла моего проекта, который очень большой, он выдает какую-то строку мусора, подобную этой:

arg: bear,1
arg: cofe,2
arg: juice,3
arg: ^X(garbage...),57

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

[EDIT] Я немного обновил описание, так как, строго говоря, ошибка возникает, когда я передал более 6 аргументов в va_test.Я понял, что первые шесть аргументов по 64 бита передаются по регистру на машине amd64, а остальные аргументы передаются по стеку.Проблема возникает, когда va_arg пытается получить первый аргумент из *overflow_arg_area.

Ответы [ 5 ]

4 голосов
/ 15 ноября 2011

Наиболее вероятное объяснение для меня состоит в том, что вы находитесь в системе, где int 0 имеет другое представление, чем char * 0. Это может быть в 64-битной системе, где sizeof(int) == 4 и * 1004.*.

Попробуйте передать последний аргумент как (char *) 0 вместо 0, и все будет в порядке.Весь остальной код выглядит технически правильным.

2 голосов
/ 15 ноября 2011

Если вы используете GCC, вы можете объявить свой va_test с помощью __attribute__((sentinel)) и убедиться, что каждое вхождение вызова завершается нулем, например,

 #define va_test(Fmt,...) va_test(Fmt,__VA_ARGS__,NULL)

Я полагаю, что ваш хаос памяти вызван тем, что какой-то вызов неNULL прекращается.

1 голос
/ 15 ноября 2011

Со страницы руководства Linux для va_arg:

If there is no next argument, or if type is not compatible with the type of the
actual next  argument  (aspromoted according to the default argument promotions),
random errors will occur.

Вам нужно найти другой способ завершения цикла, вы не можете доверять результату va_arg после того, как вы получили все аргументы.

Случайные значения, которые вы получаете, это просто то, что находится в стеке после последнего аргумента.

0 голосов
/ 15 ноября 2011

Это тот случай, когда стандарт С не очень явный.Это может быть проблематично, когда функции с переменными числами заканчиваются нулем.Для реализаций, где NULL определено как (void *)0, указатель передается в качестве аргумента, и поэтому указатель должен читаться с использованием va_arg, чтобы избежать неопределенного поведения.Если NULL определяется как просто 0 (что разрешено стандартом C, но, по моему личному опыту, это не популярное определение), тогда int передается в качестве аргумента, и поэтому int долженчитать с использованием va_arg, чтобы избежать неопределенного поведения.int и void * - это два принципиально разных типа, которые часто имеют разные размеры, поэтому это может вызвать реальные проблемы.

Если ваша реализация C определяет NULL как (void *)0, убедитесь, что предоставили NULL в вызове вашей функции вместо 0.Если вы хотите, чтобы ваш код был максимально переносимым, я бы не стал передавать 0 или NULL и вместо этого мог бы предоставить пустую строку в качестве терминатора или что-то в этом роде.

0 голосов
/ 15 ноября 2011

Добавление к Starynkevitch и Pileborg также может быть хорошей идеей заменить цикл for на некоторое время для лучшей читаемости.

while (str_arg != NULL) {
   /* Do stuff */
}

(Но замените str_arg != NULL новым конечным условием.) [Редактировать: Спасибо, Дженс.]

...