Требуются ли прототипы для всех функций в C89, C90 или C99?

StackOverflow https://stackoverflow.com/questions/434763

  •  10-07-2019
  •  | 
  •  

Вопрос

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

Это было полезно?

Решение

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

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

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

C99 запрещает «неявный int»... это означает, что оба странных случая, такие как «static a;' (ан int по умолчанию), а также объявления неявных функций.Они упоминаются (наряду с примерно 50 другими основными изменениями) в предисловии с ISO/IEC 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).

Другие советы

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

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

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

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

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

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

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

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

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

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

main (argc, argv)

  int argc;
  char **argv;

{
  ...
}

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

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

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

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

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

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

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

<Ол>
  • Мне нужно (когда A () вызывает B () , а B () вызывает A ()) или
  • Я экспортирую функцию; в противном случае это кажется излишним.
  • Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top