Требуются ли прототипы для всех функций в C89, C90 или C99? - PullRequest
44 голосов
/ 12 января 2009

Чтобы быть действительно совместимым со стандартами, должны ли все функции в C (кроме main) иметь прототип, даже если они используются только после их определения в той же единице перевода?

Ответы [ 6 ]

37 голосов
/ 13 января 2009

Это зависит от того, что вы подразумеваете под «действительно соответствием стандартам». Тем не менее, короткий ответ: «Хорошая идея - убедиться, что все функции имеют прототип в области видимости перед использованием».

В более квалифицированном ответе отмечается, что если функция принимает переменные аргументы (особенно семейство функций printf()), то прототип должен находиться в области видимости, чтобы строго соответствовать стандартам. Это верно для C89 (из ANSI) и C90 (из ISO; так же, как C89, за исключением нумерации разделов). Однако, кроме функций 'varargs', функции, которые возвращают int, не должны быть объявлены, а функции, которые возвращают что-то отличное от int, нуждаются в объявлении, которое показывает возвращаемый тип, но не нуждается в прототипе список аргументов.

Обратите внимание, однако, что если функция принимает аргументы, которые подлежат «нормальному продвижению» в отсутствие прототипов (например, функция, которая принимает char или short - оба из них преобразуются в int; более серьезно, возможно, функция, которая принимает float вместо double), тогда нужен прототип. Стандарт был довольно слабым, чтобы позволить старому коду на C компилироваться под стандартные совместимые компиляторы; старый код не был написан, чтобы беспокоиться о том, чтобы функции были объявлены перед использованием - и по определению, старый код не использовал прототипы, так как они не стали доступны в C, пока не появился стандарт.

C99 запрещает «неявное int» ... это означает как странные случаи, такие как «static a;» (по умолчанию int), так и неявные объявления функций. Они упомянуты (наряду с примерно 50 другими важными изменениями) в предисловии к ИСО / МЭК 9899: 1999, который сравнивает этот стандарт с предыдущими версиями:

  • удалить неявное int
  • удалить неявное объявление функции

В ISO / IEC 9899: 1990, §6.3.2.2 Функциональные вызовы указано:

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

extern int identifier();

появился. 38

38 То есть идентификатор с областью действия блока, объявленный как имеющий внешнюю связь с функцией типа без информация о параметрах и возвращение int. Если на самом деле оно не определено как имеющее функцию типа « возвращая int, ”поведение не определено.

Этот абзац отсутствует в стандарте 1999 года. Я (пока) не отслеживал изменение словоблудия, которое позволяет static a; в C90 и запрещает его (требующий static int a;) в C99.

Обратите внимание, что если функция является статической, она может быть определена до того, как ее использовать, и ей не обязательно должно предшествовать объявление. GCC можно убедить, что нестатическая функция определена без объявления перед ней (-Wmissing-prototypes).

14 голосов
/ 24 августа 2013

A prototype - это объявление функции, которое определяет типы параметров функции.

Pre-ANSI C (язык, описанный в первом выпуске Kernighan & Ritchie "The C Programming Language" 1978 года), не имел прототипов; для объявления функции было невозможно описать количество или типы параметров. Вызывающий абонент должен передать правильное количество и тип аргументов.

ANSI C представила «прототипы», объявления, которые определяют типы параметров (функция, заимствованная из раннего C ++).

Начиная с C89 / C90 (стандарты ANSI и ISO описывают один и тот же язык), законно вызывать функцию без видимого объявления; неявное объявление предоставляется. Если неявное объявление несовместимо с фактическим определением (скажем, вызывая sqrt("foo"), то поведение не определено. Ни это неявное объявление, ни объявление, не являющееся прототипом, не может быть совместимо с функцией с переменным числом, поэтому любой вызов функции с переменным числом ( как printf или scanf) должен иметь видимый прототип.

C99 отбросил неявные объявления. Любой вызов функции без видимого объявления является нарушением ограничения, требующего диагностики компилятора. Но это объявление все еще не обязательно должно быть прототипом; это может быть объявление старого стиля, в котором не указываются типы параметров.

C11 не внес существенных изменений в этой области.

Таким образом, даже в соответствии со стандартом ISO C 2011 года объявления и определения функций старого стиля (которые «устарели» с 1989 года) все еще разрешены в соответствующем коде.

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

13 голосов
/ 12 января 2009

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

1 голос
/ 06 сентября 2015

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

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

1 голос
/ 13 января 2009

Да, каждая функция должна иметь прототип, но этот прототип может появиться либо в отдельном объявлении, либо как часть определения функции. Определения функций, написанные на C89 и выше, естественно, имеют прототипы, но если вы пишете вещи в классическом стиле K & R, то:

main (argc, argv)

  int argc;
  char **argv;

{
  ...
}

тогда определение функции не имеет прототипа. Если вы пишете стиль ANSI C (C89), таким образом:

main (int argc, char **argv) { ... }

тогда у определения функции есть прототип.

0 голосов
/ 12 января 2009

Насколько мне известно (в ANSI C89 / ISO C90), нет. Я не уверен насчет C99; Впрочем, я бы ожидал того же.

Личное примечание: Я пишу прототипы функций только тогда, когда ...

  1. Мне нужно (когда A () вызывает B () , а B () вызывает A ()) или
  2. Я экспортирую функцию; в противном случае это кажется излишним.
...