2016-05-20 3 views
3

У меня есть функция foo(char *n, ...); Мне нужно получить и использовать все необязательные параметры char. У меня была идея использоватьКак определить конец списка va_arg?

while(va_arg(argPtr, char) != NULL) 
{ 
    ... 
} 

, чтобы понять, когда я добираюсь до конца списка. Итак, будет ли это работать, если в вызове функции я сделаю foo(n, 't', 'm', '$', NULL);?

Будет ли NULL считаться символом va_arg? Или, может быть, есть более общий способ определения конца списка, не добавляя NULL в качестве последнего параметра?

+0

если 'n' является строка формата, то вы обычно просто вести себя как' printf' и семьи путем сопоставления числа спецификаторов формата от числа параметров. Вы уверены, что это просто переменная часть 'char' передается? Если это так, вы можете сопоставлять «-1» или «0» или что-то в этом роде, но NULL - это плохая идея по причинам, изложенным в @hvd в его комментариях ниже. –

+0

@kekyc, если вы полагаетесь на отрицательные значения 'char', тогда вы должны использовать' signed char', если вы имеете дело только со значениями в диапазоне [0, 128] (ascii), тогда -1 должно быть хорошо. –

+2

Если вы передаете список аргументов 'char' переменной длины, почему бы просто не передать строку? (Есть * некоторые * случаи, когда это не сработало, но это очевидное решение.) –

ответ

5

direct способ для функции, которая использует va_arg для определения количества или типов аргументов, переданных данным вызовом.

Ваш предложенный метод, в частности:

while(va_arg(argPtr, char) != NULL) 

неверен. va_arg(argPtr, char) дает значение типа char, а NULL - константа нулевого указателя. (NULL обычно определяются как 0, который сравнивает равно нулевой символ '\0', но вы не можете полагаться на это.)

Любой VARIADIC функция должна иметь способ для вызывающего абонента, чтобы указать количество и тип аргументов. Например, функции *printf выполняют это посредством строки (невариантного) формата. Функции POSIX execl*() ожидают последовательность аргументов char*; конец списка аргументов отмечен вызывающим абонентом с (char*)NULL. Другие методы возможны, но они почти все зависят от информации, заданной во время выполнения аргументов. (Вы можете использовать использовать какой-либо другой метод, такой как глобальная переменная. Пожалуйста, не делайте этого.)

Это накладывает нагрузку на вызывающего абонента, чтобы гарантировать, что аргументы, переданные функции, являются согласованными. Сама функция не может подтвердить это. Неверные вызовы, например printf("%d\n", "hello") или execlp("name", "arg1"), есть не определено поведение.

Еще одна вещь: вы не можете использовать va_arg с аргументом типа char. Когда вы вызываете вариационную функцию, аргументы, соответствующие , ..., являются , продвигаемые. Целочисленные аргументы типов, более узкие, чем int, составляют int или unsigned int, а аргументы типа float - double. Если вызывающий объект передает аргумент типа char, функция должна вызывать va_arg(argPtr, int).

(в очень неясных обстоятельствах, которые вы, вероятно, столкнется, char может быть повышенно до unsigned int нет. Это может произойти только тогда, когда обычная char беззнаковая и sizeof (int) == 1, что означает, что байты по меньшей мере, 16 бит).

+2

Очень красиво и полно. Неясные методы включают глобальную переменную или _something_ среди аргументов (не обязательно конечный дозорный). Тем не менее, два способа, упомянутые в этом прекрасном ответе, являются наиболее распространенными. – chux

+0

@chux: Хорошая точка в отношении глобальных переменных. Я добавил соответствующие ласковые слова. –

+0

Я помню эти слабые методы кодирования с тех дней, когда разрешалось 'foo (...)' - pre C89. – chux

1

Основная идея будет работать. Но вы заполнили детали таким образом, который почти наверняка не будет.

Обычные неявные правила преобразования не применяются, когда вы используете переменные аргументы.Вместо этого происходит рекламных акций по умолчанию, что означает, что любой целочисленный тип, меньший, чем int/unsigned int, преобразуется в один из них - это не единственная рекламная кампания, но единственная, что здесь подходит, а это также означает, что есть нет автоматического преобразования в любой тип, указанный вами с помощью va_arg.

Это означает, что:

  • Вы не можете когда-либо использовать va_arg(..., char), так как невозможно передать char как VARIADIC аргумента функции.
  • Вы не можете передавать NULL в качестве аргумента вариационной функции, так как его конкретный тип сильно зависит от реализации. Реалистичные типы: int, long, void *, и есть множество других менее реалистичных, но одинаково допустимых типов.

Вы можете изменить

while(va_arg(argPtr, char) != NULL) 

в

while(va_arg(argPtr, int) != 0) 

и вызов

foo(n, 't', 'm', '$', NULL); 

в

foo(n, 't', 'm', '$', 0); 
Смежные вопросы