Я только что натолкнулся на чей-то C-код, который меня смущает, почему он компилируется. Есть два момента, которые я не понимаю.
Во-первых, прототип функции не имеет параметров по сравнению с фактическим определением функции. Во-вторых, параметр в определении функции не имеет типа.
#include <stdio.h>
int func();
int func(param)
{
return param;
}
int main()
{
int bla = func(10);
printf("%d", bla);
}
Почему это работает? Я проверил его в нескольких компиляторах, и он отлично работает.
c
parameters
function-prototypes
function-parameter
AdmiralJonB
источник
источник
-Wstrict-prototypes
обоимиint func()
иint main()
: xc: 3: warning: объявление функции не является прототипом. Вы должны объявить ,main()
какmain(void)
и.int func();
совместим сint func(arglist) { ... }
.int main(void)
.Ответы:
Все остальные ответы верны, но только для завершения
И снова ради полноты. Из спецификации C11 6: 11: 6 (стр: 179)
источник
В С
func()
означает, что вы можете передать любое количество аргументов. Если вы не хотите аргументов, вы должны объявить какfunc(void)
. Тип, который вы передаете своей функции, если не указан, по умолчаниюint
.источник
func(42,0x42);
(где все вызовы должны использовать форму двух аргументов).unsigned
это просто другое имя для того же типа, что иunsigned int
.int func();
является устаревшим объявлением функции со времен, когда не было стандарта C, т.е. дней K & R C (до 1989 года, когда был опубликован первый стандарт "ANSI C").Помните, что в K & R C не было прототипов, а ключевое слово
void
еще не было изобретено. Все, что вы могли сделать, это сообщить компилятору о типе возвращаемого значения функции. Пустой список параметров в K & R C означает «неопределенное, но фиксированное» количество аргументов. Фиксированный означает, что вы должны вызывать функцию с одинаковым количеством аргументов каждый раз (в отличие от функции с переменным числом, напримерprintf
, где число и тип могут варьироваться для каждого вызова).Многие компиляторы будут диагностировать эту конструкцию; в частности
gcc -Wstrict-prototypes
, вам скажут, что «объявление функции не является прототипом», что очень важно, потому что выглядит как прототип (особенно если вы отравлены C ++!), но это не так. Это объявление типа возврата K & R C старого стиля.Правило большого пальца. Никогда не оставляйте пустое объявление списка параметров пустым, используйте
int func(void)
для конкретности. Это превращает объявление возвращаемого типа K & R в правильный прототип C89. Компиляторы счастливы, разработчики счастливы, статические контролеры счастливы. Те, кто вводит в заблуждение ^ W ^ Wfond из C ++, могут съежиться, потому что им нужно вводить дополнительные символы, когда они пытаются использовать свои навыки иностранного языка :-)источник
int
.Я бы посчитал, что в любой сборке, в которой это происходит, не хватает настроенного уровня предупреждения / ошибки, хотя нет никакого смысла в том, чтобы это учитывало фактический код.
источник
Это объявление и определение функции в стиле K & R. От стандарта C99 (ISO / IEC 9899: TC3)
Раздел 6.7.5.3 Деклараторы функций (включая прототипы)
Раздел 6.11.6 Объявление функций
Раздел 6.11.7 Определения функций
Какой старый стиль означает K & R стиль
Пример:
Декларация:
int old_style();
Определение:
источник
C предполагает,
int
что тип не указан в типе возвращаемого значения функции и списке параметров . Только для этого правила возможны следующие странные вещи.Определение функции выглядит следующим образом.
Если его прототип вы пишете
В прототипе вы можете указать только тип параметров. Имя параметра не обязательно. Так
Также, если вы не указываете тип параметра, но имя
int
принимается как тип.Если вы идете дальше, то тоже работает.
Компилятор предполагает,
int func()
когда вы пишетеfunc()
. Но не помещайтеfunc()
в тело функции. Это будет вызов функцииисточник
int func()
не является неявной формойint func(int)
.Как сказал @Krishnabhadra, все предыдущие ответы других пользователей имеют правильную интерпретацию, и я просто хочу сделать более подробный анализ некоторых моментов.
В Old-C, как и в ANSI-C, « нетипизированный формальный параметр », возьмите измерение вашего рабочего регистра или возможности глубины команд (теневые регистры или совокупный цикл команд), в 8-битном MPU будет int16, в 16-битном MPU и так далее будет int16 и так далее, в случае, если 64-битные архитектуры могут выбрать компиляцию таких параметров, как: -m32.
Хотя это кажется более простой реализацией на высоком уровне, для передачи нескольких параметров работа программиста на шаге типа данных управления измерением становится более сложной.
В других случаях для некоторых микропроцессорных архитектур настраиваемые компиляторы ANSI использовали некоторые из этих старых функций для оптимизации использования кода, заставляя расположение этих «нетипизированных формальных параметров» работать внутри или вне рабочего регистра, сегодня вы получаете почти то же самое с использованием «volatile» и «register».
Но следует отметить, что самые современные компиляторы не делают различий между двумя типами объявления параметров.
Примеры компиляции с gcc под linux:
В любом случае, утверждение прототипа локально не имеет смысла, потому что нет вызова без параметров, ссылка на этот прототип будет бесполезной. Если вы используете систему с «нетипизированным формальным параметром», для внешнего вызова перейдите к созданию декларативного типа данных прототипа.
Нравится:
источник
int
, обычно это размер рабочего регистра или 16 бит, в зависимости от того, что меньше.int
тип, который не был способен одновременно удерживать все значения в диапазоне - От 32767 до +32767 (примечание -32768 не требуется), а также может содержать всеchar
значения (это означает, что если значениеchar
равно 16 битам, оно должно быть подписано илиint
должно быть больше).Что касается типа параметра, здесь уже есть правильные ответы, но если вы хотите услышать его от компилятора, вы можете попробовать добавить некоторые флаги (флаги почти всегда являются хорошей идеей).
компилируя вашу программу, используя
gcc foo.c -Wextra
:странно
-Wextra
не улавливает этоclang
(не распознает-Wmissing-parameter-type
по какой-то причине, может быть, для исторических, упомянутых выше), но-pedantic
делает:А для проблемы с прототипом, как уже было сказано выше, это
int func()
относится к произвольным параметрам, если вы не определите их явно, так какint func(void)
это даст вам ошибки, как и ожидалось:или
clang
как:источник
Если объявление функции не имеет параметров, т. Е. Пусто, оно принимает неопределенное количество аргументов. Если вы хотите, чтобы он не принимал аргументов, измените его на:
источник
Вот почему я обычно советую людям компилировать их код:
Эти флаги поддерживают несколько вещей:
Эти флаги также используются по умолчанию во многих проектах с открытым исходным кодом. Например, во FreeBSD эти флаги включены при сборке с WARNS = 6 в вашем Makefile.
источник