Почему функция без параметров (по сравнению с фактическим определением функции) составить?
Я только что наткнулся на чей-то код C, который я смущен тем, почему он компилируется. Есть два момента, которых я не понимаю.
во-первых, прототип функции не имеет параметров по сравнению с фактическим определением функции. Во-вторых, параметр в определении функции не имеет типа.
#include <stdio.h>
int func();
int func(param)
{
return param;
}
int main()
{
int bla = func(10);
printf("%d", bla);
}
почему это работает? Я протестировал его в нескольких компиляторах, и он отлично работает.
11 ответов
все остальные ответы верны, но только для завершение
функция объявляется следующим образом:
return-type function-name(parameter-list,...) { body... }
return-type - тип переменной, который возвращает функция. Это не может быть тип массива или тип функции. если не дано, то int предполагается.
В C func()
означает, что вы можете передать любое количество аргументов. Если вам не нужны аргументы, вы должны объявить как func(void)
. Тип, который вы передаете своей функции, если не указано значение по умолчанию int
.
int func();
является устаревшим объявлением функции со дней, когда не было стандарта C, т. е. дней K&R C (до 1989 года, когда был опубликован первый стандарт "ANSI C").
помните, что было нет прототипов в K&R C и ключевое слово void
еще не изобрели. Все, что вы могли сделать, это рассказать компилятору о тип возвращаемого функции. Пустой список параметров в K&R C означает " неопределенный, но исправлено " количество аргументов. Fixed означает, что вы должны вызвать функцию с то же самое количество args каждый раз (в отличие от variadic как 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 определения функциииспользование деклараторов функций с пустыми скобками (не параметр prototype-format деклараторы типов) - это отживающие функции.
использование определений функций с отдельными идентификаторами параметров и списками объявлений (не тип параметра prototype-format и деклараторы идентификаторов) является отживающие функции.
что означает старый стиль K & R стиль
пример:
объявления: int old_style();
определение:
int old_style(a, b)
int a;
int b;
{
/* something to do */
}
C предполагает int
если тип не задан для типа возврата функции и списка параметров. Только для этого правила возможны следующие странные вещи.
определение функции выглядит следующим образом.
int func(int param) { /* body */}
если его прототип вы пишите
int func(int param);
в prototype вы можете указать только тип параметров. Имя параметров не является обязательным. Так что
int func(int);
также, если вы не указываете тип параметра, но имя int
считается типа.
int func(param);
если вы идете дальше, следующие работы тоже.
func();
компилятор предполагает int func()
когда вы пишите func()
. Но не ставьте func()
внутри тела функции. Это будет вызов функции
в Старом-C, как в ANSI-C "нетипизированный формальный параметр", возьмите dimencion вашего рабочего регистра или возможности глубины инструкции (теневые регистры или накопительный цикл инструкции), в 8-битном MPU, будет int16, в 16-битном MPU и так будет int16 и так далее, в случае 64bit архитектуры могут выбрать для компиляции такие параметры, как:- m32.
хотя это кажется более простой реализацией на высоком уровне, Для параметров пропуска множественных, работа программника в шаге типа данных dimencion управления, будет более требовательной.
в других случаях для некоторых архитектур микропроцессоров компиляторы ANSI, настроенные, использовали некоторые из этих старых функций для оптимизации использования кода, заставляя расположение этих " нетипизированных формальных параметры " для работы внутри или вне рабочего реестра, сегодня вы получаете почти то же самое с использованием "volatile" и "register".
но следует отметить, что большинство современных компиляторов, не делайте никакого различия между двумя типами объявления параметров.
примеры компиляции с gcc под linux:
в любом случае заявление прототип локально бесполезен, потому что нет вызова без параметров, ссылка на этот прототип будет упущена.
Если вы используете систему с "нетипизированным формальным параметром"для внешнего вызова, перейдите к созданию декларативного прототипа типа данных.
такой:
int myfunc(int param);
что касается типа параметра, здесь уже есть правильные ответы, но если вы хотите услышать его от компилятора, вы можете попробовать добавить некоторые флаги (флаги почти всегда хорошая идея в любом случае).
компиляция вашей программы с помощью gcc foo.c -Wextra
Я:
foo.c: In function ‘func’:
foo.c:5:5: warning: type of ‘param’ defaults to ‘int’ [-Wmissing-parameter-type]
странно -Wextra
не ловит это для clang
(он не распознает -Wmissing-parameter-type
по какой-то причине, возможно, для исторических, упомянутых выше), но -pedantic
тут:
foo.c:5:10: warning: parameter 'param' was not declared,
defaulting to type 'int' [-pedantic]
int func(param)
^
1 warning generated.
и для прототипа вопрос, как сказано выше int func()
относится к произвольным параметрам, если вы exclicitly определить его как int func(void)
который затем даст вам ошибки, как ожидалось:
foo.c: In function ‘func’:
foo.c:6:1: error: number of arguments doesn’t match prototype
foo.c:3:5: error: prototype declaration
foo.c: In function ‘main’:
foo.c:12:5: error: too many arguments to function ‘func’
foo.c:5:5: note: declared here
или clang
as:
foo.c:5:5: error: conflicting types for 'func'
int func(param)
^
foo.c:3:5: note: previous declaration is here
int func(void);
^
foo.c:12:20: error: too many arguments to function call, expected 0, have 1
int bla = func(10);
~~~~ ^~
foo.c:3:1: note: 'func' declared here
int func(void);
^
2 errors generated.
Если объявление функции не имеет параметров, т. е. пусто, то оно принимает неопределенное количество аргументов. Если вы хотите, чтобы он не принимал никаких аргументов, измените его на:
int func(void);
вот почему я обычно советую людям компилировать свой код с помощью:
cc -Wmissing-variable-declarations -Wstrict-variable-declarations -Wold-style-definition
эти флаги обеспечивают выполнение нескольких вещей:
- - Wmissing-variable-declarations: невозможно объявить нестатическую функцию, не получив сначала прототип. Это делает более вероятным, что прототип в файле заголовка соответствует фактическому определению. Кроме того, он обеспечивает добавление ключевого слова static в функции, которые не должны быть видимыми публично.
- - wstrict-variable-declarations: прототип должен правильно перечислить аргументы.
- - Wold-style-definition: само определение функции также должно правильно перечислять аргументы.
эти флаги также используется по умолчанию во многих проектах с открытым исходным кодом. Например, во FreeBSD эти флаги включены при построении с WARNS=6 в вашем файле Makefile.
сэр, в C++ (и только C++) вы можете определить несколько функций с одинаковым именем с разными параметрами. Например:
int func();
int func(int test);
int func(char testing123);
следует подготовить. Чтобы выбрать, какую функцию использовать, просто передайте этот тип переменной в скобки при компиляции.
например:
int testing123=2;
func(testing123);
вызовет func (int test).
, тогда как
char test='a';
func(test);
вызовет func (char).
вам не нужно имена переменных в заголовке функции, хотя до тех пор, пока прототип функции (вы знаете, строка вверху, которая имеет только функцию без кода в ней) соответствует именам в фактической функции ниже, вы будете в порядке (например, вместо int func(int)
вы могли бы также иметь int func(int avariable).
Что касается переменной в компиляции прототипа без типа, она, вероятно, по умолчанию имеет тип, вероятно, int (хотя я не уверен, какой тип по умолчанию зависит от компилятора или нет.)