Почему функция без параметров (по сравнению с фактическим определением функции) составить?

Я только что наткнулся на чей-то код 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 деклараторы функций

использование деклараторов функций с пустыми скобками (не параметр prototype-format деклараторы типов) - это отживающие функции.

6.11.7 определения функции

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

main.c

main2.c

main3.c  
в любом случае заявление прототип локально бесполезен, потому что нет вызова без параметров, ссылка на этот прототип будет упущена. Если вы используете систему с "нетипизированным формальным параметром"для внешнего вызова, перейдите к созданию декларативного прототипа типа данных.

такой:

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 (хотя я не уверен, какой тип по умолчанию зависит от компилятора или нет.)