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


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

пример:

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

6 ответов:

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

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

будет та же функция.

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

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

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

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

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

}

в этом случае определение функции преобразует представленный параметр из double (превращенная форма) в 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 должен быть предпочтительным, если у вас есть выбор, потому что он избавляется от старого определения стиля спереди. Если такие противоречащие типы функций для функции появляются в одной и той же единице перевода, компилятор обычно сообщит вам (но не обязательно). Если такие противоречия появляются по нескольким единицам перевода, ошибка, возможно, пойдет незамеченным и может привести к трудно предсказать ошибки. Лучше всего избегать этих старых определений стиля.

Это так абонента K & R style или старый стиль декларации.

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

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

#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

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

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