Альтернативный (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?

Ответ 1

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

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

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-style. Я обращаюсь к тому факту, что функция может вызываться с "замененными" аргументами, т.е. В таком вызове не происходит проверки типа параметра. Это очень мало связано с определением K & R-style per se, но оно имеет все, что связано с тем, что ваша функция не имеет прототипа. Вы видите, в C, когда вы объявляете такую ​​функцию

int foo();

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

foo(2, 3);

и как

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

ans и так далее (вы получаете идею);

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

Собственно, это поведение является особенностью языка 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();

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

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

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

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

int f()
{
  return 0;
}

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

int f(void)
{
  return 0;
}

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

Ответ 2

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

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

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

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

Ответ 3

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

Составители поддерживают это, чтобы быть полным, и, конечно, иметь возможность обрабатывать старые базы кода.

Ответ 4

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

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

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

Ответ 5

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