Введение в язык Си++


Незаданное Число Параметров


Для некоторых функций невозможно задать число и тип всех параметров, которые можно ожидать в вызове. Такую функцию описывают завершая список описаний параметров многоточием (...), что означает "и может быть, еще какие-то параметры". Например:

int printf(char* ...);

Это задает, что в вызове printf должен быть по меньшей мере один параметр, char*, а остальные могут быть, а могут и не быть. Например:

printf("Hello, world\n"); printf("Мое имя %s %s\n", first_name, second_name); printf("%d + %d = %d\n",2,3,5);

Такая функция полагается на информацию, которая недоступна компилятору при интерпретации ее списка параметров. В случае printf() первым параметром является строка формата, содержащая специальные последовательности символов, позволяющие printf() правильно обрабатывать остальные параметры. %s означает "жди параметра char*", а %d означает "жди параметра int". Однако, компилятор этого не знает, поэтому он не может убедиться в том, что ожидаемые параметры имеют соответствующий тип. Например:

printf("Мое имя %s %s\n",2);

откомпилируется и в лучшем случае приведет к какой-нибудь странного вида выдаче.

Очевидно, если параметр не был описан, то у компилятора нет информации, необходимой для выполнения над ним проверки типа и преобразования типа. В этом случае char или short передаются как int, а float передается как double. Это не обязательно то, чего ждет пользователь.

Чрезмерное использование многоточий, вроде wild(...), полностью выключает проверку типов параметров, оставляя программиста открытым перед множеством неприятностей, которые хорошо знакомы программистам на C. В хорошо продуманной программе требуется самое большее несколько функций, для которых типы параметров не определены полностью. Для того, чтобы позаботиться о проверке типов, можно использовать перегруженные функции и функции с параметрами по умолчанию в большинстве тех случаев, когда иначе пришлось бы оставить типы параметров незаданными. Многоточие необходимо только если изменяются и число параметров, и тип параметров. Наиболее обычное применение многоточия в задании интерфейса с функциями C библиотек, которые были определены в то время, когда альтернативы не было:


extern int fprintf(FILE*, char* ...); // из extern int execl(char* ...); // из extern int abort(...); // из

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

void error(int ...);

main(int argc, char* argv[]) { switch(argc) { case 1: error(0,argv[0],0); break; case 2: error(0,argv[0],argv[1],0); default: error(1,argv[0],"с",dec(argc-1),"параметрами",0); } }

Функцию ошибок можно определить так:

#include

void error(int n ...) /* "n" с последующим списком char*, оканчивающихся нулем */ { va_list ap; va_start(ap,n); // раскрутка arg

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

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


Содержание раздела