int imax(int, int);
int imaxdnt а, int b);
В первой форме применяется список типов, разделенных запятыми. Во второй форме к типам добавлены имена переменных. Помните, что имена переменных являются фиктивными и не обязаны соответствовать именам, используемым в определении функции.
Располагая этой информацией, компилятор может проверить, совпадает ли вызов функции с прототипом. Указано ли правильное количество аргументов? Имеют ли они корректные типы? В случае несовпадения типов, когда оба типа являются числовыми, компилятор преобразует значения фактических аргументов в типы формальных параметров. Например, вызов imax (3.0, 5.0) становится imax (3, 5). Мы модифицировали код в листинге 9.4 для применения прототипа функции. Результат представлен в листинге 9.5.
340 глава 9
Листинг 9.5. Программа proto.с

Попытка компиляции программы из листинга 9.5 приводит к выдаче компилятором сообщения об ошибке, указывающего на то, что вызов imax() содержит слишком мало параметров.
А что можно сказать об ошибках, связанных типами? Для их исследования мы заменили imax(3) вызовом imax(3, 5) и попробовали скомпилировать программу еще раз. На этот раз сообщения об ошибках не было, и мы запустили программу на выполнение. Ниже показан результирующий вывод:
Наибольшим значением из 3 и 5 является 5.
Наибольшим значением из 3 и 5 является 5.
Как и ожидалось, 3.0 и 5.0 во втором вызове были преобразованы в 3 и 5, чтобы функция могла должным образом обработать входные данные.
Хотя сообщения об ошибке отсутствовали, компилятор выдал предупреждение о том, что тип double был преобразован в тип int и возможна потеря данных. Например, вызов
imax(3.9, 5.4)
становится эквивалентным следующему вызову:
imax(3, 5)
Разница между сообщением об ошибке и предупреждением заключается в том, что ошибка предотвращает компиляции, а предупреждение — нет. Некоторые компиляторы выполняют такое приведение типа, не информируя вас. Причина связана с тем, что стандарт не требует вывода предупреждений. Однако многие компиляторы позволяют выбирать уровень выдачи предупреждений, который управляет многословностью компилятора при сообщении о предупреждениях.
Отсутствие аргументов и неопределенные аргументы
Предположим, что вы создали прототип следующего вида:
void print_name();
Компилятор ANSI С предположит, что вы решили воспользоваться стилем объявления, предшествующим прототипированию, и не будет проверять аргументы. Для отражения того, что функция не принимает аргументов, укажите в круглых скобках ключевое слово void:
void print_name(void);
Функции 341
Компилятор ANSI С интерпретирует предыдущее выражение как то, что функция print name() не имеет аргументов. Затем он проверяет, не применяете ли вы аргументы при вызове этой функции.
Некоторые функции, такие как printf() и scanf(), принимают переменное количество аргументов. Например, в printf() первым аргументом является строка, но остальные аргументы не фиксированы ни по типам, ни по количеству. Для таких случаев стандарт ANSI С разрешает частичное прототипирование. Например, для printf() можно было бы использовать такой прототип:
int printf(const char *, ... );
Этот прототип указывает, что первым аргументом является строка (данный аспект объясняется в главе 11) и что могут быть указаны дальнейшие аргументы с неопределенной природой.
Заголовочный файл stdarg.h в библиотеке функций С предоставляет стандартный способ для определения функции с переменным количеством параметров; детали будут раскрыты в главе 16.
преимущество прототипов
Прототипы являются мощным дополнением к языку. Они позволяют компилятору выявлять многие ошибки или оплошности, которые могли быть допущены при использовании функций. Если их не обнаружить своевременно, они превратятся в проблемы, которые могут оказаться трудными для отслеживания. Обязаны ли вы применять прототипы? Нет, вы вполне можете использовать старый метод объявления функций (без указания параметров), но никакими преимуществами он не обладает, взамен имея множество недостатков.
Существует один способ избежать прототипа, одновременно сохранив преимущества прототипирования. Причиной указания прототипа является сообщение компилятору о том, как должна использоваться функция, до достижения им первого фактического случая ее применения. Того же результата можно добиться, поместив полное определение функции до ее первого использования. В этом случае определение действует как собственный прототип. Чаще всего это делается с короткими функциями:
Читать дальше