web-dev-qa-db-fra.com

Syntaxe de la fonction C, types de paramètres déclarés après la liste de paramètres

Je suis relativement nouveau en C. J'ai rencontré une forme de syntaxe de fonction jamais vue auparavant, où les types de paramètres sont définis après cette liste de paramètres. Est-ce que quelqu'un peut m'expliquer en quoi c'est différent de la syntaxe typique de la fonction C?

Exemple: 

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

C'est la syntaxe à l'ancienne pour les listes de paramètres, qui est toujours prise en charge. Dans K & R C, vous pouvez également laisser de côté les déclarations de type, qui seraient par défaut à int. c'est à dire.

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

serait la même fonction.

58
Ferruccio

Ce qui est également intéressant, c’est la différence de convention d’appel des fonctions avec, et des fonctions sans prototype. Considérons une définition de style ancien:

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

Dans ce cas, la convention d'appel stipule que tous les arguments sont promus avant d'être passés à la fonction. Donc, si f reçoit une double mais que le paramètre a le type float (ce qui est parfaitement valide), le compilateur doit émettre un code qui convertit le double en float avant l'exécution du corps de la fonction.

Si vous incluez un prototype, le compilateur ne fait plus de telles promotions automatiques et toutes les données transmises sont converties en types des paramètres du prototype comme si elles étaient affectées. Donc, ce qui suit n'est pas légal et entraîne un comportement indéfini:

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

}

Dans ce cas, la définition de la fonction convertira le paramètre soumis de double (le formulaire promu) en float car la définition est de style ancien. Mais le paramètre a été soumis en tant que float, car la fonction a un prototype. Vos options pour résoudre les contradictions sont les deux suivantes:

// 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) {

}

L'option 2 est préférable si vous avez le choix, car elle supprime l'ancienne définition du style. Si de tels types de fonctions contradictoires pour une fonction apparaissent dans la même unité de traduction, le compilateur vous le dira généralement (mais ce n'est pas obligatoire). Si de telles contradictions apparaissent sur plusieurs unités de traduction, l'erreur passera probablement inaperçue et peut rendre difficile la prédiction de bogues. Il est préférable d'éviter ces définitions de style ancien.

25

C'est l'appelant style K & R ou old-style déclaration.

Notez que cette déclaration est significativement différente de la déclaration moderne. La déclaration K & R n'introduit pas de prototype pour la fonction, ce qui signifie qu'elle n'expose pas les types de paramètres au code extérieur.

10
AnT

Bien que l'ancienne syntaxe de définition de fonction fonctionne toujours (avec des avertissements, si vous demandez à votre compilateur), leur utilisation ne fournit pas de prototypes de fonction.
Sans prototypes de fonctions, le compilateur ne vérifiera pas si les fonctions sont appelées correctement.

#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;
}

Lorsque le programme est exécuté, la sortie sur ma machine est

$ 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
4
pmg

Il n'y a pas de différence, c'est juste que c'est l'ancienne syntaxe pour les déclarations de fonction en C - elle était utilisée avant ANSI. Ne jamais écrire un tel code, sauf si vous envisagez de le donner à vos amis des années 80 . En outre, ne dépend jamais de suppositions de type implicites (comme une autre réponse semble le suggérer)

3
aviraldg

C'est juste la même chose mais à l'ancienne. Vous avez probablement trouvé qu'il s'agit d'un vieux code hérité.

1
eyalm

Vieux ou pas, je dirais ce qui est vieux et ce qui, comme les pyramides sont anciennes, mais aucun des soi-disant scientifiques d'aujourd'hui n'a une idée de la façon dont elles ont été fabriquées. Rétrospectivement, les anciens programmes fonctionnent encore aujourd'hui sans fuites de mémoire, mais ces "nouveaux" programmes ont tendance à échouer plus souvent. Je vois une tendance ici.

Ils ont probablement vu les fonctions comme des structures ayant un corps exécutable. La connaissance de l'ASM est nécessaire ici pour résoudre le mystère.

Edit, a trouvé une macro qui indique que vous n'avez pas besoin de fournir de noms d'arguments.

#ifndef OF /* function prototypes */
#  ifdef STDC
#    define OF(args)  args
#  else
#    define OF(args)  ()
#  endif
#endif

#ifndef Z_ARG /* function prototypes for stdarg */
#  if defined(STDC) || defined(Z_HAVE_STDARG_H)
#    define Z_ARG(args)  args
#  else
#    define Z_ARG(args)  ()
#  endif
#endif

Voici un exemple d'utilisation, library est zlib-1.2.11 .

ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));

Donc, ma deuxième hypothèse serait pour la surcharge de fonctions, sinon ces arguments n'auraient aucune utilité. Une fonction concrète, et maintenant un nombre infini de fonctions portant le même nom.

0
Ako