Синтаксис функции C, типы параметров, объявленные после списка параметров

Я относительно не знаком с C. Я столкнулся с формой функционального синтаксиса, который я никогда раньше не видел, где типы параметров определяются после этого списка параметров. Может ли кто-нибудь объяснить мне, как он отличается от типичного синтаксиса функции C?

Пример:

int main (argc, argv)
int argc;
char *argv[];
{
return(0);
}

Ответ 1

Это синтаксис старого стиля для списков параметров, который по-прежнему поддерживается. В K & R C вы также можете оставить объявления типа, и они будут по умолчанию для int. то есть.

main(argc, argv)
char *argv[];
{
    return 0;
}

будет той же самой функцией.

Ответ 2

То, что также интересно, это различие между вызовами функций и функциями без прототипа. Рассмотрим старое определение стиля:

void f(a)
 float a; {
 /* ... */
}

В этом случае вызывающее соглашение состоит в том, что все аргументы продвигаются перед передачей функции. Поэтому, если f получает double, но параметр имеет тип float (что совершенно верно) компилятор должен испускать код, который преобразует double в float до выполнения тела функции.

Если вы включаете прототип, компилятор больше не делает таких автоматических рекламных акций, и любые переданные данные преобразуются в типы параметров прототипа, как если бы они были назначены. Итак, следующее не является законным и приводит к поведению undefined:

void f(float a);
void f(a)
  float a; {

}

В этом случае определение функции преобразует представленный параметр из double (продвигаемой формы) в float, потому что определение является старым. Но параметр был представлен как float, потому что функция имеет прототип. Ваши варианты решения противоречий следующие:

// option 1
void f(double a);
void f(a)
  float a; {

}

// option 2
// this declaration can be put in a header, but is redundant in this case, 
// since the definition exposes a prototype already if both appear in a 
// translation unit prior to the call. 
void f(float a); 

void f(float a) {

}

Вариант 2 должен быть предпочтительнее, если у вас есть выбор, потому что он избавляется от определения старого стиля спереди. Если такие противоречивые типы функций для функции отображаются в одной и той же единицы перевода, компилятор обычно скажет вам (но не обязательно). Если такие противоречия появятся над несколькими единицами перевода, ошибка, возможно, останется незамеченной и может привести к затруднению прогнозирования ошибок. Лучше избегать этих старых определений стиля.

Ответ 3

Это так называемый код K & R или декларация старого стиля.

Обратите внимание, что это выражение существенно отличается от современной декларации. Объявление K & R не вводит прототип функции, а это означает, что он не предоставляет типы параметров внешнему коду.

Ответ 4

Пока старый синтаксис определения функции по-прежнему работает (с предупреждениями, если вы спросите своего компилятора), использование их не предоставляет прототипов функций.
Без прототипов функций компилятор не будет проверять правильность вызова функций.

#include <stdio.h>
int foo(c)
int c;
{ return printf("%d\n", c); }

int bar(x)
double x;
{ return printf("%f\n", x); }

int main(void)
{
    foo(42); /* ok */
    bar(42); /* oops ... 42 here is an `int`, but `bar()` "expects" a `double` */
    return 0;
}

Когда программа запущена, вывод на моем компьютере

$ gcc proto.c
$ gcc -Wstrict-prototypes proto.c
proto.c:4: warning: function declaration isn’t a prototype
proto.c:10: warning: function declaration isn’t a prototype
$ ./a.out
42
0.000000

Ответ 5

Нет никакой разницы, просто это старый синтаксис для объявлений функций в C - он использовался до ANSI. Никогда не пишите такой код, если вы не планируете передавать его своим друзьям из 80-х годов. Кроме того, никогда не зависит от неявных допущений типа (как кажется, кажется, другой ответ)

Ответ 6

Его просто такая же, но старая мода. Вероятно, вы обнаружили, что это старый, устаревший код.