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

2.6 – Предварительные объявления и определения

Взгляните на этот, казалось бы, правильный пример программы:

Вы ожидаете, что эта программа даст результат:

Но на самом деле она вообще не компилируется! Visual Studio выдает следующую ошибку компиляции:

Причина, по которой эта программа не компилируется, заключается в том, что компилятор последовательно компилирует содержимое исходных файлов. Когда компилятор достигает вызова функции add в строке 5 в функции main , он не знает, что такое add , потому что мы определили add только в строке 9! Это вызывает ошибку, « identifier not found » (идентификатор не найден).

Более старые версии Visual Studio выдали бы дополнительную ошибку:

Это несколько вводит в заблуждение, учитывая, что add в первый раз вообще не была определена. Несмотря на это, в целом полезно отметить, что довольно часто одна ошибка вызывает множество повторяющихся или связанных ошибок или предупреждений.

Лучшая практика

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

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

Вариант 1. Изменение порядка определений функций

Один из способов решения проблемы – переупорядочить определения функций так, чтобы add была определена перед main :

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

Кроме того, этот вариант не всегда возможен. Допустим, мы пишем программу, которая имеет две функции A и B . Если функция A вызывает функцию B , а функция B вызывает функцию A , то нет способа упорядочить функции таким образом, чтобы компилятор был доволен. Если вы сначала определите A , компилятор пожалуется, что не знает, что такое B . Если вы сначала определите B , компилятор пожалуется, что не знает, что такое A .

Вариант 2. Использование предварительного объявления

Мы также можем исправить это, используя предварительное объявление.

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

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

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

Вот прототип функции для функции add :

Итак, вот наша исходная программа, которая не компилировалась, использующая прототип функции в качестве предварительного объявления для функции add :

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

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

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

Лучшая практика

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

Забываем о теле функции

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

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

Рассмотрим следующую программу:

В этой программе мы даем предварительное объявление add и вызываем add , но нигде не определяем add . Когда мы пытаемся скомпилировать эту программу, Visual Studio выдает следующее сообщение:

Как видите, программа скомпилировалась нормально, но проблема возникла на этапе компоновки, потому что int add(int, int) никогда не определялась.

Другие типы предварительных объявлений

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

Объявления против определений

В C++ вы часто будете слышать слова «объявление» и «определение», часто взаимозаменяемые. Что они имеют в виду? Теперь у вас достаточно знаний, чтобы понять разницу между ними.

Определение фактически реализует (для функций или типов) или создает экземпляр (для переменных) идентификатора. Вот несколько примеров определений:

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

Правило одного определения (или сокращенно ODR, one definition rule) – это хорошо известное правило в C++. ODR состоит из трех частей:

  1. В заданном файле функция, объект, тип или шаблон могут иметь только одно определение.
  2. В заданной программе объект или обычная функция может иметь только одно определение. Это выделено потому, что программы могут иметь более одного файла (мы рассмотрим это в следующем уроке).
  3. Типы, шаблоны, встроенные функции и переменные могут иметь идентичные определения в разных файлах. Мы еще не рассмотрели большинство из этих вещей, поэтому не беспокойтесь об этом сейчас – мы вернемся к ним, когда это будет уместно.

Нарушение пункта 1 правила одного определения приведет к тому, что компилятор выдаст ошибку переопределения. Нарушение пункта 2 правила одного определения может привести к тому, что компоновщик выдаст ошибку переопределения. Нарушение пункта 3 правила одного определения приведет к неопределенному поведению.

Вот пример нарушения пункта 1:

Поскольку указанная выше программа нарушает пункт 1 правила одного определения, компилятор Visual Studio выдает следующие ошибки компиляции:

Для продвинутых читателей

Функции, которые имеют общий идентификатор, но имеют разные параметры, считаются отдельными функциями. Мы обсудим это далее в уроке «8.9 – Перегрузка функций».

Объявление – это инструкция, которая сообщает компилятору о существовании идентификатора и информацию о его типе. Вот несколько примеров объявлений:

Объявление – это всё, что нужно компилятору. Вот почему мы можем использовать предварительное объявление, чтобы сообщить компилятору об идентификаторе, который на самом деле не будет определен позже.

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

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

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

Примечание автора

В обычном языке термин «объявление» обычно используется для обозначения «чистого объявления», а «определение» используется для обозначения «определения, которое также служит объявлением». Таким образом, мы обычно называем int x; определением, хотя это и определение, и объявление.

Небольшой тест

Вопрос 1

Что такое прототип функции?

Прототип функции – это инструкция объявления, которая включает в себя имя функции, тип возвращаемого значения и параметры. Он не включает в себя тело функции.

Вопрос 2

Что такое предварительное объявление?

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

Вопрос 3

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

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

Вопрос 4

Напишите прототип для этой функции (используйте предпочтительную форму с именами):

Вопрос 5

Для каждой из следующих программ укажите, завершится ли их компиляция неудачей, завершится ли их линковка неудачей, или будут успешными и компиляция, и линковка. Если не уверены, попробуйте их скомпилировать!

Не компилируется. Компилятор будет жаловаться, что add() , вызываемая в main() , не имеет того же количества параметров, что и та, что была предварительно объявлена.

Не компилируется. Компилятор будет жаловаться, что не может найти подходящую функцию add() , которая принимает 3 аргумента, потому что функция add() , которая была предварительно объявлена, принимает только 2 аргумента.

Не линкуется. Компилятор сопоставит предварительно объявленный прототип add с вызовом функции add() в main() . Однако функция add() , которая принимает два параметра, никогда не была реализована (мы реализовали только ту, которая принимает 3 параметра), поэтому компоновщик будет жаловаться.

Компилируется и линкуется. Вызов функции add() соответствует прототипу, который был предварительно объявлен, реализованная функция также совпадает.

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

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

Связанные понятия

Стиль о́тступов (индентация) — правила форматирования исходного кода, в соответствии с которыми отступы программных блоков проставляются в удобочитаемой манере.

В программировании, ассемблерной вставкой называют возможность компилятора встраивать низкоуровневый код, написанный на ассемблере, в программу, написанную на языке высокого уровня, например, Си или Ada. Использование ассемблерных вставок может преследовать следующие цели.

Парсер (англ. parser; от parse – анализ, разбор) или синтаксический анализатор — часть программы, преобразующей входные данные (как правило, текст) в структурированный формат. Парсер выполняет синтаксический анализ текста.

В языках программирования объявле́ние (англ. declaration) включает в себя указание идентификатора, типа, а также других аспектов элементов языка, например, переменных и функций. Объявление используется, чтобы уведомить компилятор о существовании элемента; это весьма важно для многих языков (например, таких как Си), требующих объявления переменных перед их использованием.

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

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

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

Содержание

Пример

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

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

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

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

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

Функция «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  

См. также

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

Wikimedia Foundation . 2010 .

Полезное

Смотреть что такое «Прототип функции» в других словарях:

Прототип — (от др. греч. πρῶτος  первый и τύπος  отпечаток, оттиск; прообраз, образец), Prototype: Прототип (когнитивная психология)  абстрактный образ, воплощающий множество сходных форм одного и того же объекта или паттерна, наиболее… … Википедия

прототип — ПРОТОТИП (от греч. prototypon прообраз) в когнитивистике лучший представитель («лучший пример») когнитивной или языковой категории. Теория прототипов разрабатывалась параллельно в когнитивной психологии (Л.С. Выготский, Э. Рош),… … Энциклопедия эпистемологии и философии науки

strcpy — strcpy  функция стандартной библиотеки языка программирования Си, для копирования нуль терминированной строки (включая нуль терминатор) в заданный буфер . Содержание 1 Прототип функции 2 Возвращаемое значение … Википедия

WinMain — функция, в которой программист пишет основной код, который будет выполнять программа под Windows для подсистемы GUI. Эта функция вызывается из функции WinMainCRTStartup (находящейся в CRT), которая по умолчанию является точкой входа в программу… … Википедия

Объявление (информатика) — Возможно, эта статья содержит оригинальное исследование. Добавьте ссылки на источники, в противном случае она может быть выставлена на удаление. Дополнительные сведения могут быть на странице обсуждения. (25 мая 2011) … Википедия

Strcpy — функция стандартной библиотеки языка программирования Си, для копирования нуль терминированной строки в заданный буфер. Содержание 1 Прототип функции 2 Возвращаемое значение … Википедия

Экспериментальная модель — Прототип (от греч. protos первый и typos отпечаток, оттиск) прообраз, образец, оригинал. Прототип м. греч. первообраз, начальный, основной образец, истинник. Прототипный, типический, первообразный, первообразцовый (Словарь Даля). Прототип в… … Википедия

D (язык программирования) — У этого термина существуют и другие значения, см. D. D Семантика: мультипарадигменный: императивное, объектно ориентированное, обобщённое программирование Тип исполнения: компилятор Появился в: 1999 Автор(ы) … Википедия

Feof — feof  функция стандартной библиотеки языка Си, объявленная в заголовочном файле stdio.h. Ее основное назначение  отличать случаи, когда операции потока достигают конца файла, от случаев, когда возвращается код ошибки EOF («конец файла» … Википедия

DLL — (англ. Dynamic link library  динамически подключаемая библиотека)  понятие операционных систем Microsoft Windows и IBM OS/2; динамическая библиотека, позволяющая многократное применение различными программными приложениями. K DLL… … Википедия

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

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

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

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

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