Препроцессор и библиотека С 709
Переменное число аргументов: файл stdarg.h
Ранее в этой главе обсуждались макросы с переменным числом аргументов. Заголовочный файл stdarg.h предоставляет похожую возможность для функций. Однако использовать ее немного сложнее. Вы должны выполнить следующие действия.
1. Подготовить прототип функции, в котором применяется троеточие.
2. Создать в определении функции переменную типа va_list.
3. Использовать макрос для инициализации этой переменной списком аргументов.
4. Применить макрос для доступа к списку аргументов.
5. Использовать макрос для очистки.
Давайте рассмотрим эти действия более подробно. Прототип для функции подобного рода должен иметь список, содержащий, по крайней мере, один параметр, за которым следует троеточие:
void f 1 (int n, ...); // допустимо
int f2(const char * s, int k, ...); // допустимо
char f3(char cl, char c2); // недопустимо, троеточие не в конце
double f3(...); /./ недопустимо, параметры отсутствуют
Крайний справа параметр (предшествующий троеточию) играет специальную роль; для его обозначения в стандарте используется термин parmN. В предшествующих примерах роль parmN играл параметр n в первом случае и к — во втором. Фактическим аргументом, передаваемым этому параметру, является количество аргументов, которые представлены разделом троеточия. Например, прототипироваиную ранее функцию fl() можно вызывать следующим образом:
f1 (2, 200, 400); //2 дополнительных аргумента
f1 (4, 13, 117, 18, 23); // 4 дополнительных аргумента
Тип va_list, объявленный в заголовочном файле stdargs.li, представляет объект данных, применяемый для хранения параметров, которые соответствуют разделу троеточия в списке параметров. Начало определения функции с переменным числом аргументов выглядит примерно так:
double sum(int lim,...)
{
va_list ар; // объявление объекта для хранения аргументов
В этом примере lim является параметром parmN и указывает количество аргументов в списке переменных-аргументов.
Затем функция будет использовать макрос va start(), также определенный в stdargs.li, для копирования списка аргументов в переменную va_list. Макрос принимает два аргумента: переменную va_list и параметр parmN. Продолжая предыдущий пример, переменная va list названа ар, а параметру parmN назначено имя lim, так что вызов будет иметь следующий вид:
va_start(ар, lim); // инициализация ар списком аргументов
На следующем этапе производится доступ к содержимому списка аргументов. Эго предусматривает применение еще одного макроса, va_arg(), который принимает два аргумента: переменную типа va list и имя типа. При первом вызове он возвращает первый элемент списка, при следующем вызове — следующий элемент списка и т.д.
710 Глава 16
Например, если первым аргументом в списке был double, а вторым — int, вы мог ли бы поступить так:

Но будьте внимательны. Тип аргумента на самом деле должен соответствовать спецификации. Если первым аргументом является 10.0, предыдущий код для tic работает нормально. Однако если аргументом оказывается 10, код может не заработать; автоматическое преобразование double в int, предпринимаемое для операции присваивания, здесь не происходит.
Наконец, вы должны провести очистку с помощью макроса va_end(). Например, может понадобиться освободить память, динамически выделенную для хранения аргументов. Этот макрос принимает в качестве аргумента переменную va_list:
va_end(ap); // очистка
После этого переменная ар может оказаться непригодной к употреблению до тех пор, пока вы не инициализируете ее повторно посредством макроса va_start.
Поскольку макрос va_arg() не обеспечивает копирование предыдущих аргументов для их возможного восстановления, может оказаться целесообразным сохранение копии переменной va list. Для этой цели в стандарте С99 предусмотрен макрос по имени va copy(). Он принимает два аргумента типа va list и копирует второй аргумент в первый:

На данном этапе по-прежнему можно извлечь первые два элемента из арсору, несмотря на то, что они были удалены из ар.
В листинге 16.21 приведен краткий пример использования этих возможностей для создания функции, которая суммирует переменное число аргументов; здесь первым аргументом sum() является количество суммируемых элементов.
Листинг 16.21. Программа varargs.c
Читать дальше