Что называется прототипом функции
Перейти к содержимому

Что называется прототипом функции

Прототипы функций

Прототипом функции в языке Си или C++ называется объявление функции, не содержащее тела функции, но указывающее имя функции, арность, типы аргументов и возвращаемый тип данных. В то время как определение функции описывает, что именно делает функция, прототип функции может восприниматься как описание её интерфейса.

double new_style(int a, double *x); /* прототип функции */

В прототипе имена аргументов являются необязательными, тем не менее, необходимо указывать тип вместе со всеми модификаторами (например, указатель ли это или константный аргумент).

double alt_style(int, double *); /* альтернативная форма прототипа */

Урок №19. Прототип функции и Предварительное объявление

На этом уроке мы рассмотрим прототип функции и предварительное объявление в языке С++.

Оглавление:

  1. Наличие проблемы
  2. Прототипы функций и Предварительное объявление
  3. Предварительно объявили, но не определили
  4. Объявление vs. Определение
  5. Тест
  6. Ответы

Наличие проблемы

Посмотрите на этот, казалось бы, невинный кусочек кода под названием add.cpp:

std :: cout << "The sum of 3 and 4 is: " << add ( 3 , 4 ) << std :: endl ; int add ( int x , int y ) return x + y ;

Вы, наверное, ожидаете увидеть примерно следующий результат:

The sum of 3 and 4 is: 7

Но в действительности эта программа даже не скомпилируется. Причиной этому является то, что компилятор читает код последовательно. Когда он встречает вызов функции add() в строке №5 функции main(), он даже не знает, что такое add(), так как это еще не определили! В результате чего мы получим следующую ошибку:

add: идентификатор не найден

Чтобы устранить эту проблему, мы должны учитывать тот факт, что компилятор не знает, что такое add(). Есть 2 решения.

Решение №1: Поместить определение функции add() выше её вызова (т.е. перед функцией main()):

int add ( int x , int y )
return x + y ;
std :: cout << "The sum of 3 and 4 is: " << add ( 3 , 4 ) << std :: endl ;

Таким образом, при вызове функции add() в функции main(), компилятор будет знать, что это такое. Так как это простая программа, то внести подобные изменения несложно. Однако в программах, содержащих большое количество строк кода, это может быть утомительно — узнавать кто кого вызывает и в каком порядке (чтобы соблюсти правильную последовательность).

Кроме того, этот вариант не всегда возможен. Например, мы пишем программу, которая имеет две функции: А() и В() . Если функция А() вызывает функцию В() , а функция В() вызывает функцию А() , то нет никакого способа упорядочить эти функции таким образом, чтобы они обе одновременно знали о существовании друг друга. Если вы объявите сначала А() , то компилятор будет жаловаться, что не знает, что такое В() . Если вы объявите сначала В() , то компилятор будет жаловаться, что не знает, что такое А() .

Прототипы функций и Предварительное объявление

Решение №2: Использовать предварительное объявление.

Предварительное объявление сообщает компилятору о существовании идентификатора ДО его фактического определения.

В случае функций, мы можем сообщить компилятору о существовании функции до её фактического определения. Для этого нам следует использовать прототип этой функции. Прототип функции (полноценный) состоит из типа возврата функции, её имени и параметров (тип + имя параметра). В кратком прототипе отсутствуют имена параметров функции. Основная часть (между фигурными скобками) опускается. А поскольку прототип функции является стейтментом, то он также заканчивается точкой с запятой.

Вот прототип функции add():

int add ( int x , int y ) ; // прототип функции состоит из типа возврата функции, её имени, параметров и точки с запятой

А вот вышеприведенная программа, но уже с прототипом функции в качестве предварительного объявления аdd():

Использование прототипов функции

Стандарт ANSI С расширяет концепцию предварительного описания функции. Данное расширенное описание называется прототипом функции.

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

тип имя_функции (список параметров);

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

Помимо этого, для информирования компилятора о типе возвращаемого функцией значения прототип функции позволяет С осуществлять качественную проверку типов, наподобие проверки, осуществляемой в таких языках, как Паскаль. Прототипы позволяют компилятору найти и сообщить информацию о всех незаконных преобразованиях типов между типами аргументов, используемых при вызове функции, и типами определенных параметров. Также компилятору разрешается сообщать о том, что функция вызывается с неправильным числом аргументов.

Если возможно, С автоматически преобразует тип аргумента в тип, получаемый параметром. Тем не менее, некоторые преобразования типов недопустимы. Если функция имеет прототип, то все нелегальные преобразования будут найдены и появится сообщение об ошибке. В качестве примера, следующая программа вызывает сообщение об ошибке, поскольку пытается вызвать func() с указателем, а не с требуемым float. (Нельзя преобразовать указатель к типу float.)

/* Данная программа использует прототипы функций для достижения строгой проверки типов при вызове func(). Программа не компилируется из-за несоответствия между типом аргументов, определенных в прототипе функции, и типом аргументов, используемых при вызове функции. */

#include
float func (int x, float у); /* прототип */
int main(void)
int x, *y;
x = 10;
у = &x;
func(x, у) ; /* несоответствие типов */
return 0;
>

float func (int x, float y)
printf(«%f», у/(float)x);
return у/(float) x;
>

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

/* Программа не компилируется из-за несоответствия между числом параметров, определенных в прототипе функции, и числом аргументов, используемых при вызове функции. */

#include
float func (int x, float у); /* прототип */
int main(void)
func (2, 2.0, 4); /* неверное число аргументов */
return 0;
>

С технической точки зрения при создании прототипа функции не требуется включать настоящие имена параметров. Например, следующие два варианта абсолютно корректны и равнозначны:

char func (char *, int);

char func (char *str, int count);

Тем не менее, если включить имена параметров, компилятор использует имена для выдачи сообщений о несоответствии типов.

Некоторые функции типа printf() могут принимать переменное число аргументов. Переменное число аргументов определяется в прототипе с помощью многоточия. Например, прототип функции printf() выглядит так:

int printf(const char *fmt, . );

Для создания функции с переменным числом аргументов надо обратиться к описанию стандартной библиотечной функции va_arg().

Следует использовать прототипы функций для предотвращения ошибок, появляющихся в результате вызовов функций с неправильными аргументами. Они также помогают проверить правильность работы программы, не позволяя функциям вызываться с неправильным числом аргументов. Как ранее упоминалось, они требуются в С++.

  • Прототипы стандартных библиотечных функций
  • Создание прототипов функций, не имеющих параметров

Прототип функции

Прототипом функции в языке Си или C++ называется объявление функции, которое не содержит тело функции, но указывает имя функции, арность, типы аргументов и возвращаемый тип данных. В то время как определение функции описывает, что именно делает функция, прототип функции может восприниматься как описание её интерфейса.

В прототипе имена аргументов являются необязательными, тем не менее, необходимо указывать тип вместе со всеми модификаторами (например, указатель ли это или константный аргумент).

Пример

В качестве примера, рассмотрим следующий прототип функции:

int foo(int n); 

Этот прототип объявляет функцию с именем «foo», которая принимает один аргумент «n» целого типа и возвращает целое число. Определение функции может располагаться где угодно в программе, но определение требуется только в случае её использования.

Использование

Уведомление компилятора

Если функция предварительно не была объявлена, а её имя встречается в выражении, следующим за открывающей скобкой, то она неявно объявляется как функция, возвращающая результат типа int и ничего не предполагается о её аргументах. В этом случае компилятор не сможет выполнить проверку типов аргументов и арность, когда функция вызывается с некоторыми аргументами. Это потенциальный источник проблем. Следующий код иллюстрирует ситуацию, в которой поведение неявно объявленной функции не определено.

#include /* * При реализации этого прототипа компилятор выдаст сообщение об ошибке * в main(). Если он будет пропущен, то и сообщения об ошибке не будет. */ int foo(int n); /* Прототип функции */ int main(void) /* Вызов функции */  printf("%d\n", foo()); /* ОШИБКА: у foo отсутствует аргумент! */ return 0; > int foo(int n) /* Вызываемая функция */  if (n == 0) return 1; else return n * foo(n - 1); > 

Функция «foo» ожидает аргумент целого типа, находящийся в стеке при вызове. Если прототип пропущен, компилятор не может это обработать и «foo» завершит операцию на некоторых других данных стека (вероятно, это будет обратный адрес или значение переменной, не входящей в область допустимых значений). Включением прототипа функции вы информируете компилятор о том, что функция «foo» принимает один аргумент целого типа и вы тем самым позволяете компилятору обрабатывать подобные виды ошибок.

Создание библиотечных интерфейсов

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

Объявления класса

В C++ прототипы функций также используются в определении классов.

Ссылки

  • Kernighan, Brian W. & Ritchie, Dennis M. (1988), «The C Programming Language» (2nd ed.), Upper Saddle River, NJ: Prentice Hall PTR, ISBN 0131103628

См. также

  • Сигнатура типа
  • Язык программирования Си

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *