Вопрос, который вы задаете, на самом деле состоит из двух вопросов, а не из одного. Большинство ответов до сих пор пытались покрыть все это общим общим ответом «это стиль 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