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

Что полезно в этом синтаксисе 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?

Ответы [ 5 ]

142 голосов
/ 27 октября 2009

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

int foo();

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

foo(2, 3);

и как

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

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

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

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

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 func вы все равно можете назвать 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 требует, чтобы функция была объявлена ​​перед первым использованием, но объявление не обязательно должно быть прототипом. Эта путаница, очевидно, связана с популярной терминологической путаницей: многие люди называют любое объявление функции «прототипом», тогда как на самом деле «объявление функции» - это не то же самое, что «прототип».

17 голосов
/ 27 октября 2009

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

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

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

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

4 голосов
/ 27 октября 2009

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

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

1 голос
/ 27 октября 2009

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

1 голос
/ 27 октября 2009

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

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

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

...