Язык программирования C++ для профессионалов


Неопределенное число параметров - часть 2


extern "C" int fprintf(FILE*, const char* ...); extern "C" int execl(const char* ...);

Есть стандартный набор макроопределений, находящийся в <stdarg.h>, для выбора незаданных параметров этих функций. Рассмотрим функцию реакции на ошибку, первый параметр которой показывает степень тяжести ошибки. За ним может следовать произвольное число строк. Нужно составить сообщение об ошибке с учетом, что каждое слово из него передается как отдельная строка:

extern void error(int ...) extern char* itoa(int);

main(int argc, char* argv[]) { switch (argc) { case 1: error(0,argv[0],(char*)0); break; case 2: error(0,argv[0],argv[1],(char*)0); break; default: error(1,argv[0], "With",itoa(argc-1),"arguments",(char*)0); } // ... }

Функция itoa возвращает строку символов, представляющую ее целый параметр. Функцию реакции на ошибку можно определить так:

#include <stdarg.h>

void error(int severity ...) /* за "severity" (степень тяжести ошибки) следует список строк, завершающийся нулем */ { va_list ap; va_start(ap,severity); // начало параметров

for (;;) { char* p = va_arg(ap,char*); if (p == 0) break; cerr << p << ' '; }

va_end(ap); // очистка параметров

cerr << '\n'; if (severity) exit(severity); }

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

Приведение 0 к (char*)0 необходимо потому, что sizeof(int) не обязано совпадать с sizeof(char*). Этот пример демонстрирует все те сложности, с которыми приходится сталкиваться программисту, если он решил обойти контроль типов, используя эллипсис.




Начало  Назад  Вперед



Книжный магазин