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

375

Я только что наткнулся на чей-то код, который я смущен, почему он компилируется. Есть два момента, которые я не понимаю.

Во-первых, прототип функции не имеет параметров по сравнению с фактическим определением функции. Во-вторых, параметр в определении функции не имеет типа.

#include <stdio.h>

int func();

int func(param)
{
    return param;
}

int main()
{
    int bla = func(10);    
    printf("%d", bla);
}

Почему это работает? Я тестировал его в нескольких компиляторах, и он отлично работает.

  • 74
    Это K & R C. Мы написали подобный код в 1980-х годах, прежде чем появились полнофункциональные прототипы.
  • 0
    удивил, что не было никаких предупреждений ..
Показать ещё 14 комментариев
Теги:
parameters
function-prototypes
function-parameter

11 ответов

278
Лучший ответ

Все остальные ответы верны, но только для завершения

Функция объявляется следующим образом:

  return-type function-name(parameter-list,...) { body... }

return-type - это тип переменной, возвращаемый функцией. Это не может быть тип массива или тип функции. Если не указано, то int предполагается.

имя-функции - это имя функции.

список параметров - это список параметров, которые функция разделяет запятыми. Если параметры не заданы, то функция не принимает и должен быть определен с пустым множеством скобки или ключевое слово void. Если тип переменной не находится впереди переменной в списке paramater, тогда int предполагается. Массивы и функции не передаются в функции, а автоматически преобразуются указателям. Если список заканчивается эллипсисом (,...), то нет заданного количества параметров. Примечание: заголовок stdarg.h может быть используется для доступа к аргументам при использовании многоточия.

И снова ради полноты. Из спецификации C11 6: 11: 6 (страница: 179)

использование деклараторов функций с пустыми скобками (не прототип типа форматов деклараторов) является устаревшим Функция.

  • 1
    «Если тип переменной не находится перед переменной в списке параметров, то предполагается использование int». Я вижу это в предоставленной вами ссылке, но не могу найти ее ни в одном стандартном c89, c99 ... Можете ли вы предоставить другой источник?
164

В C func() означает, что вы можете передать любое количество аргументов. Если вам не нужны аргументы, вы должны объявить как func(void). Тип, который вы передаете своей функции, если не указан по умолчанию, int.

  • 3
    На самом деле нет единого типа по умолчанию для int. На самом деле все аргументы по умолчанию имеют значение int (даже возвращаемый тип). Совершенно нормально называть func(42,0x42); (где все вызовы должны использовать форму двух аргументов).
  • 1
    В Си довольно часто для неопределенных типов значение по умолчанию равно int, например, когда вы объявляете переменную: unsigned x; Какого типа переменная x? Оказывается, его без знака Int
Показать ещё 5 комментариев
53

int func(); является объявлением устаревающей функции с тех дней, когда не было стандартного C, т.е. дней K & RC (до 1989 года, когда был опубликован первый стандарт ANSI C).

Помните, что в K & R C не было прототипов, а ключевое слово void еще не было изобретено. Все, что вы могли сделать, это сообщить компилятору о возвращаемом типе функции. Пустой список параметров в K & R C означает "неопределенное, но фиксированное" количество аргументов. Исправлено означает, что вы должны вызывать функцию с таким же количеством аргументов каждый раз (в отличие от вариационной функции типа printf, где число и тип могут меняться для каждого вызова).

Многие компиляторы будут диагностировать эту конструкцию; в частности, gcc -Wstrict-prototypes скажет вам, что "объявление функции не является прототипом", на котором находится точка, потому что он похож на прототип (особенно если вы отравлены С++!), но это не так. Это декларация типа старого типа K & R C.

Правило большого пальца: Никогда не оставляйте пустое объявление списка параметров пустым, используйте int func(void) для конкретного. Это превращает объявление типа K & R в правильный прототип C89. Составители довольны, разработчики довольны, статические шашки счастливы. Тем не менее, они вводят в заблуждение W ^ Wfond of С++, потому что им нужно вводить дополнительные символы, когда они пытаются использовать свои навыки иностранного языка: -)

  • 29
    Черт возьми, я такая старая школа, что я посмотрел на пример и не понял смущения автора, так как не увидел в этом ничего плохого.
  • 1
    Пожалуйста, не связывайте это с C ++. Здравый смысл заключается в том, что пустой список аргументов означает отсутствие параметров , и люди во всем мире не заинтересованы иметь дело с ошибками, допущенными парнями из K & R около 40 лет назад. Но эксперты из комитетов продолжают тащить странные решения - в C99, C11.
Показать ещё 1 комментарий
51
  • Пустой список параметров означает "любые аргументы", поэтому определение не является неправильным.
  • Недопустимый тип считается int.

Я бы рассмотрел любую сборку, которая пропускает это, чтобы не хватать настроенного уровня предупреждения/ошибки, хотя нет смысла в том, чтобы это позволяло использовать фактический код.

29

Это объявление K & R стиля и определение стиля. Из стандарта C99 (ISO/IEC 9899: TC3)

Раздел 6.7.5.3 Деклараторы функций (включая прототипы)

Список идентификаторов объявляет только идентификаторы параметров функции. Пустое список в объявлении функции, который является частью определения этой функции, указывает, что функция не имеет параметров. Пустой список в объявлении функции, который не является частью определение этой функции указывает, что никакой информации о количестве или типах параметры. (Если оба типа функций являются "старым стилем", типы параметров не сравниваются.)

Раздел 6.11.6 Объявление функций

Использование деклараторов функций с пустыми круглыми скобками (а не параметром прототипа тип деклараторы) является <сильным > устаревшим признаком.

Раздел 6.11.7 Определения функций

Использование определений функций с отдельными идентификаторами параметров и списками деклараций (не тип файла типа прототипа и деклараторы идентификатора) является устаревшей функцией.

Какой старый стиль означает стиль K & R

Пример:

Объявление: int old_style();

Определение:

int old_style(a, b)
    int a; 
    int b;
{
     /* something to do */
}
14

C принимает int, если тип типа не указан в списке функций и списке параметров. Только для этого правила возможны следующие странные вещи.

Определение функции выглядит следующим образом.

int func(int param) { /* body */}

Если его прототип вы пишете

int func(int param);

В прототипе вы можете указать только тип параметров. Имя параметра не является обязательным. Так

int func(int);

Также, если вы не укажете тип параметра, но имя int предполагается как тип.

int func(param);

Если вы пойдете дальше, следуйте за работами.

func();

Компилятор принимает int func() при написании func(). Но не помещайте func() внутри тела функции. Это будет вызов функции

  • 1
    Пустой список параметров не связан с неявным типом int. int func() не является неявной формой int func(int) .
11

Как указано @Krishnabhadra, все предыдущие ответы от других пользователей имеют правильную интерпретацию, и я просто хочу сделать более подробный анализ некоторых точек.

В Old-C, как и в ANSI-C, " нетипизированный формальный параметр", возьмите размер вашего регистра регистрации работы или глубины команды (теневые регистры или кумулятивный цикл команд), в 8 бит MPU, будет int16, в 16-битном MPU и так будет int16 и так далее, в случае, если 64-битные архитектуры могут выбрать такие параметры, как: -m32.

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

В других случаях для некоторых архитектур микропроцессоров, настроенных компиляторами ANSI, использовались некоторые из этих старых функций для оптимизации использования кода, заставляя местоположение этих "нетипизированных формальных параметров" работать внутри или вне рабочего реестра, сегодня вы получаете почти то же самое с использованием "volatile" и "register".

Но следует отметить, что самые современные компиляторы, не делают различий между объявлением двух типов параметров.

Примеры компиляции с gcc в linux:

Изображение 4794

Изображение 4795

Изображение 4796

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

Вот так:

int myfunc(int param);
  • 2
    Г! Не размещайте большие скриншоты, если вам действительно не нужно ...
  • 5
    Я потрудился оптимизировать изображения для того, чтобы размер в байтах изображений был минимально возможным. Я думаю, что картинки могут быстро увидеть отсутствие различий в сгенерированном коде. Я тестировал код, генерировал реальную документацию о том, что он требовал, а не просто теоретизировал о проблеме. но не все видят мир одинаково. Мне ужасно жаль, что моя попытка предоставить больше информации сообществу так сильно вас потревожила.
Показать ещё 5 комментариев
5

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

компиляция вашей программы с помощью 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() относится к произвольным параметрам, если вы явно не определяете ее как 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 как:

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.
3

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

int func(void);
  • 1
    «Если у прототипа функции нет параметров» Это не прототип функции, а просто объявление функции.
  • 0
    @effeffe исправил это. Я не понимал, что этот вопрос получит много внимания;)
Показать ещё 8 комментариев
0

Вот почему я обычно советую людям компилировать свой код с помощью:

cc -Wmissing-variable-declarations -Wstrict-variable-declarations -Wold-style-definition

Эти флаги обеспечивают выполнение нескольких действий:

  • -Wmissing-variable-declarations: невозможно объявить нестатистическую функцию, не получив сначала прототип. Это делает более вероятным, что прототип в файле заголовка совпадает с фактическим определением. Кроме того, он предусматривает, что вы добавляете ключевое слово static к функциям, которые не должны быть видимыми публично.
  • -Wstrict-variable-declarations: Прототип должен правильно перечислять аргументы.
  • -Wold-style-definition: само определение функции также должно правильно перечислять аргументы.

Эти флаги также используются по умолчанию во многих проектах с открытым исходным кодом. Например, во FreeBSD эти флаги включаются при создании с WARNS = 6 в Makefile.

-11

Сэр, в С++ (и С++ ONLY) вам разрешено определять несколько функций с тем же именем с разными параметрами. Например:

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).

Вам не нужны имена переменных в заголовке функции, хотя до тех пор, пока прототип функции (y'know, строка вверху, которая имеет только функцию без кода в ней), соответствует именам в фактической функции ниже вас, lL будет A OKay (например, вместо int func(int) вы могли бы также иметь int func(int avariable).

Что касается переменной в компиляции прототипа без типа, то, вероятно, по умолчанию используется тип, вероятно, int (хотя я не уверен, что какой тип по умолчанию он может быть изменен компилятором или нет.)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню