Альтернативный (K&R) синтаксис C для объявления функции по сравнению с прототипами

Что полезного в этом синтаксисе C — использование объявлений функций в стиле «K&R»?

int func (p, p2)
    void* p;
    int  p2;
{
    return 0;
}

Я смог написать это в Visual Studios 2010beta.

// yes, the arguments are flipped
void f()
{
    void* v = 0;
    func(5, v);
}

Я не понимаю. В чем смысл этого синтаксиса? Я могу написать:

int func (p, p2)
    int  p2;
{
    return 0;
}
// and write
int func (p, p2)
{
    return 0;
}

Единственное, что он указывает, это количество используемых параметров и тип возвращаемого значения. Я думаю, что параметры без типов — это круто, но зачем разрешать их и int paranName после декларатора функции? Это странно.

Также это все еще стандартный C?


person Community    schedule 27.10.2009    source источник
comment
Это старый стиль объявления функций. Пожалуйста, посмотрите эту ссылку. https://stackoverflow.com/questions/22500/какие-основные-различия-между-ansi-c-and-kr-c текст ссылки   -  person Leonidas Tsampros    schedule 27.10.2009


Ответы (5)


Вопрос, который вы задаете, на самом деле состоит из двух вопросов, а не из одного. Большинство ответов до сих пор пытались покрыть все это общим общим ответом «это стиль K&R», хотя на самом деле только небольшая часть его имеет какое-либо отношение к тому, что известно как стиль K&R (если вы не видите весь язык C как "K&R-стиль" в той или иной мере :)

Первая часть — это странный синтаксис, используемый в определении функции.

int func(p, p2)
void *p;
int  p2; /* <- optional in C89/90, but not in C99 */
{
  return 0;
}

На самом деле это определение функции в стиле K&R. Другой ответ довольно хорошо покрыл это. А на самом деле не так уж и много. Синтаксис устарел, но все еще полностью поддерживается даже в C99 (за исключением правила «no implicit int» в C99, что означает, что в C99 вы не можете опустить объявление p2).

Вторая часть имеет мало общего с K&R-стилем. Имею в виду, что функция может быть вызвана с "переставленными" аргументами, т.е. при таком вызове не происходит проверки типа параметра. Это имеет очень мало общего с определением в стиле K&R как таковое, но имеет прямое отношение к вашей функции, не имеющей прототипа. Видите ли, в C, когда вы объявляете такую ​​функцию

int foo();

на самом деле он объявляет функцию foo, которая принимает неопределенное количество параметров неизвестного типа. Вы можете назвать это как

foo(2, 3);

и в качестве

j = foo(p, -3, "hello world");

и так далее (вы поняли);

Только вызов с правильными аргументами будет «работать» (это означает, что другие вызовы приводят к неопределенному поведению), но вы полностью должны убедиться в его правильности. Компилятор не обязан диагностировать неправильные, даже если он каким-то волшебным образом знает правильные типы параметров и их общее количество.

На самом деле такое поведение является особенностью языка C. Опасная, но тем не менее особенность. Это позволяет вам сделать что-то вроде этого

void foo(int i);
void bar(char *a, double b);
void baz(void);

int main()
{
  void (*fn[])() = { foo, bar, baz };
  fn[0](5);
  fn[1]("abc", 1.0);
  fn[2]();
}

то есть смешивать разные типы функций в «полиморфном» массиве без каких-либо приведения типов (однако здесь нельзя использовать вариативные типы функций). Опять же, опасности, присущие этой методике, совершенно очевидны (я не помню, чтобы когда-либо использовал ее, но могу представить, где она может быть полезна), но в конце концов это C.

Наконец, бит, который связывает вторую часть ответа с первой. Когда вы делаете определение функции в стиле K&R, оно не представляет прототип функции. Что касается типа функции, ваше определение func объявляет func как

int func();

т. е. не объявляются ни типы, ни общее количество параметров. В своем исходном сообщении вы говорите: «... кажется, указано, сколько параметров он использует ...». Формально - нет! После вашего двухпараметрического определения func в стиле K&R вы все еще можете вызывать func как

func(1, 2, 3, 4, "Hi!");

и в нем не будет нарушения ограничений. (Обычно качественный компилятор выдает предупреждение).

Кроме того, иногда упускается из виду тот факт, что

int f()
{
  return 0;
}

также является определением функции в стиле K&R, которое не вводит прототип. Чтобы сделать его «современным», вам нужно явно указать void в списке параметров.

int f(void)
{
  return 0;
}

Наконец, вопреки распространенному мнению, в C99 полностью поддерживаются как определения функций в стиле K&R, так и непрототипированные объявления функций. Первый устарел с C89/90, если я правильно помню. C99 требует, чтобы функция была объявлена ​​перед первым использованием, но объявление не обязательно должно быть прототипом. Путаница, по-видимому, происходит из-за популярной терминологической путаницы: многие люди называют любое объявление функции «прототипом», хотя на самом деле «объявление функции» — это не то же самое, что «прототип».

person AnT    schedule 27.10.2009
comment
Но какая теперь разница между функцией varargs и непрототипированной. - person Sebastian; 26.09.2013
comment
Варарги @Sebastian Godelet излишне строги. Например, вызов функции vararg с одним целым и извлечение ее как байтов недопустимо. То же самое и с определением функции только с параметрами vararg. - person yyny; 05.06.2016
comment
Функции @Sebastian Vararg на самом деле могут быть правильно вызваны с разным количеством параметров, например printf. Функции без прототипов могут быть вызваны правильно только с определенным фиксированным количеством аргументов, но компилятор не знает, какой из них и не может проверить (поэтому вы должны это сделать). - person not-a-user; 09.04.2019
comment
Думаю пора менять supported even in C99 . C99 to C11. :-). - person sjsam; 17.04.2019
comment
Я помню, когда я начал с C на Amstrad PCW, я был сбит с толку, потому что компилятор использовал этот старый синтаксис объявления функций, но учебник, который у меня был, использовал более новый синтаксис. Это были времена... 25 лет назад...! - person Andrew Truckle; 25.06.2019
comment
Если определение функции K&R не вводит прототип функции, в чем смысл перечисления всех аргументов функции и их типов? Вы перечислили все параметры и их типы, но все равно получаете функцию неопределенного количества параметров неизвестного типа - person aryndin; 16.07.2021
comment
@aryndin: В K&R цель перечисления параметров чисто внутренняя: сделать их доступными внутри функции. Он не имеет внешнего воздействия, т. е. не вводит прототип. - person AnT; 16.07.2021

Это довольно старый синтаксис K&R C (предшествующий ANSI/ISO C). В настоящее время вам больше не следует его использовать (поскольку вы уже заметили его главный недостаток: компилятор не будет проверять типы аргументов за вас). Тип аргумента фактически по умолчанию равен int в вашем примере.

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

foo(p, q) 
{
    return q + p;
}

что на самом деле было допустимым определением, поскольку типы для foo, p и q по умолчанию равны int.

person Dirk    schedule 27.10.2009

Это просто старый синтаксис, предшествующий синтаксису "ANSI C", с которым вы, возможно, более знакомы. Обычно он называется "K&R C".

Компиляторы поддерживают его завершенность и, конечно же, способность обрабатывать старые кодовые базы.

person unwind    schedule 27.10.2009

Это пережиток того времени, когда в C не было прототипов функций. В то время (я думаю) предполагалось, что функции возвращают int, а все аргументы — int. Проверка параметров функции не проводилась.

Вам гораздо лучше использовать прототипы функций в текущем языке C.
И вы должны использовать их в C99 (C89 по-прежнему принимает старый синтаксис) .

А C99 требует, чтобы функции были объявлены (возможно, без прототипа). Если вы пишете новую функцию с нуля, вам нужно предоставить объявление... сделать ее тоже прототипом: вы ничего не теряете и получаете дополнительную проверку от компилятора.

person pmg    schedule 27.10.2009
comment
Неправильно. В C99 функции должны быть явно объявлены до их вызова. Тем не менее, C99 не требует проротипированной декларации. C99 полностью поддерживает синтаксис K&R с одним изменением: неявное правило int было удалено. - person AnT; 27.10.2009

Это исходный синтаксис K&R до того, как C был стандартизирован в 1989 году. C89 представил прототипы функций, заимствованные из C++, и синтаксис K&R стал устаревшим. Нет причин использовать его (и много причин не использовать) в новом коде.

person Robert Gamble    schedule 27.10.2009