Требуются ли прототипы для всех функций на 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()) или
- Я экспортирую функцию; в противном случае он чувствует себя лишним.