Вопрос, который вы задаете, - это действительно два вопроса, а не один. Большинство ответов до сих пор пытались охватить всю вещь с помощью общего одеяла «это ответ K & amp; R style», в то время как на самом деле лишь малая его часть имеет какое-либо отношение к тому, что известно как стиль K & amp; R (если вы не видите весь язык C как «K & amp; R-стиль» так или иначе:)
Первая часть - странный синтаксис, используемый в определении функции
int func(p, p2)
void *p;
int p2; /* <- optional in C89/90, but not in C99 */
{
return 0;
}
Это фактически определение функции K & amp; R-стиля. Другой ответ на это довольно хорошо. И на самом деле этого мало. Синтаксис устарел, но полностью поддерживается даже в C99 (за исключением правила «no implicit int» в C99, что означает, что на C99 вы не можете опустить объявление p2
).
Второй часть имеет мало общего с K & amp; R-style. Я обращаюсь к тому факту, что функция может вызываться с «замененными» аргументами, т. Е. В таком вызове не происходит проверки типа параметра. Это очень мало связано с определением K & amp; R-style per se, но оно имеет все, что связано с тем, что ваша функция не имеет прототипа. Вы видите, что в C, когда вы объявляете такую функцию
int foo();
, она фактически объявляет функцию foo
, которая принимает неуказанное количество параметров неизвестного типа . Вы можете назвать его как
foo(2, 3);
и как
j = foo(p, -3, "hello world");
ans и так далее (вы получаете идею);
Только вызов с правильными аргументами будет «работать» (что означает, что другие производят неопределенное поведение), но это полностью зависит от вас, чтобы обеспечить его правильность. Компилятор не обязан диагностировать неправильные, даже если он каким-то образом знает правильные типы параметров и их общее число.
На самом деле это поведение является языка 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]();
}
i.e. смешивать разные типы функций в «полиморфном» массиве без каких-либо типов (типы вариационной функции здесь не могут быть использованы). Опять же, присущие опасности этой техники совершенно очевидны (я не помню, чтобы когда-либо ее использовали, но я могу представить, где это может быть полезно), но это C в конце концов.
Наконец, бит, который связывает вторая часть ответа на первый. Когда вы выполняете определение функции K & amp; R-стиля, оно не вводит прототип функции. Что касается типа функции, ваше определение func
объявляет func
как
int func();
i.e. ни типы, ни общее количество параметров не объявлены. В своем оригинальном посте вы говорите: «... кажется, это то, сколько параметров он использует ...». Формально это не так! После вашего двухпараметрического определения K & amp; R-style func
вы все еще можете вызвать func
как
func(1, 2, 3, 4, "Hi!");
, и в нем не будет никаких ограничений. (Как правило, качественный компилятор даст вам предупреждение).
Кроме того, иногда упускается из виду факт, что
int f()
{
return 0;
}
также является определением функции K & amp; R-стиля, которое не вводит прототип. Чтобы сделать его «современным», вам нужно будет поместить явный void
в список параметров
int f(void)
{
return 0;
}
. Наконец, вопреки распространенному мнению, как определения функций K & amp; R-стиля, Прототипные объявления функций полностью поддерживаются в C99. Первый был устаревшим с C89 / 90, если я правильно помню. C99 требует, чтобы функция была объявлена до первого использования, но декларация не требуется для прототипа. Путаница, по-видимому, проистекает из популярного терминологического смешения: многие люди называют любое объявление функции «прототипом», а на самом деле «объявление функции» - это не то же самое, что «прототип».
Вот готовый пример . Он аутентифицирует клиента на сервере авторизации, а также обращается к защищенному ресурсу с сервера ресурсов.
Я все еще работаю над документированием исходного кода и завершением README. Если у вас есть вопросы, не стесняйтесь спрашивать меня.