Многоточие
Иногда нельзя перечислить типы и количество всех возможных аргументов функции. В этих случаях список параметров представляется многоточием (...), которое отключает механизм проверки типов. Наличие многоточия говорит компилятору, что у функции может быть произвольное количество аргументов неизвестных заранее типов. Многоточие употребляется в двух форматах:
void foo( parm_list, ... ); |
void foo( ... );
Первый формат предоставляет объявления для части параметров. В этом случае проверка типов для объявленных параметров производится, а для оставшихся фактических аргументов – нет. Запятая после объявления известных параметров необязательна.
Примером вынужденного использования многоточия служит функция printf()
стандартной библиотеки С. Ее первый параметр является C-строкой:
int printf( const char* ... );
Это гарантирует, что при любом вызове printf() ей будет передан первый аргумент типа const char*. Содержание такой строки, называемой форматной, определяет, необходимы ли дополнительные аргументы при вызове. При наличии в строке формата метасимволов, начинающихся с символа %, функция ждет присутствия этих аргументов. Например, вызов
printf( "hello, world\n" );
имеет один строковый аргумент. Но
printf( "hello, %s\n", userName );
имеет два аргумента. Символ %
говорит о наличии второго аргумента, а буква s, следующая за ним, определяет его тип – в данном случае символьную строку.
Большинство функций с многоточием в объявлении получают информацию о типах и количестве фактических параметров по значению явно объявленного параметра. Следовательно, первый формат многоточия употребляется чаще.
Отметим, что следующие объявления неэквивалентны:
void f(); |
void f( ... );
В первом случае f()
объявлена как функция без параметров, во втором – как имеющая ноль или более параметров. Вызовы
f( someValue ); |
f( cnt, a, b, с );
корректны только для второго объявления. Вызов
f();
применим к любой из двух функций.
Упражнение 7.4
Какие из следующих объявлений содержат ошибки? Объясните.
(a) void print( int arr[][], int size ); (b) int ff( int a, int b = 0, int с = 0 ); |
(d) char *screenInit( int height = 24, int width, char background ); |
Упражнение 7.5
Повторные объявления всех приведенных ниже функций содержат ошибки. Найдите их.
(a) char *screenInit( int height, int width, char background = ' ' ); char *screenInit( int height = 24, int width, char background ); (b) void print( int (*arr)[6], int size ); void print( int (*arr)[5], int size ); (c) void manip( int *pi, int first, int end = 0 ); |
Упражнение 7.6
Даны объявления функций.
void print( int arr[][5], int size ); void operate(int *matrix[7]); char *screenInit( int height = 24, int width = 80, |
Вызовы этих функций содержат ошибки. Найдите их и объясните.
(a) screenInit(); (b) int *matrix[5]; operate( matrix ); (c) int arr[5][5]; |
Упражнение 7.7
Перепишите функцию putValues( vector<int> ), приведенную в подразделе 7.3.4, так, чтобы она работала с контейнером list<string>. Печатайте по одному значению на строке. Вот пример вывода для списка из двух строк:
( 2 )
<
"first string"
"second string"
>
Напишите функцию main(), вызывающую новый вариант putValues() со следующим списком строк:
"put function declarations in header files" "use abstract container types instead of built-in arrays" "declare class parameters as references" "use reference to const types for invariant parameters" |
Упражнение 7.8
В каком случае вы применили бы параметр-указатель? А в каком – параметр-ссылку? Опишите достоинства и недостатки каждого способа.