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

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

Ответы

Ответ 1

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

Более квалифицированный ответ отмечает, что если функция принимает переменные аргументы (в частности, семейство функций 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
    ...
  • удалить объявление неявной функции

В ИСО/МЭК 9899: 1990, §6.3.2.2 Вызов функций:

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

extern int identifier();

появилось. 38

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

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

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

Ответ 2

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

Ответ 3

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

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

ANSI C представил "прототипы", декларации, которые определяют типы параметров (элемент, заимствованный из раннего С++).

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

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

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

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

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

Ответ 4

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

main (argc, argv)

  int argc;
  char **argv;

{
  ...
}

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

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

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

Ответ 5

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

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

Ответ 6

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

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

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