Des prototypes sont-ils requis pour toutes les fonctions en C89, C90 ou C99?

StackOverflow https://stackoverflow.com/questions/434763

  •  10-07-2019
  •  | 
  •  

Question

Pour être véritablement conforme aux normes, toutes les fonctions en C (à l'exception de la fonction principale) doivent avoir un prototype, même si elles ne sont utilisées qu'après leur définition dans la même unité de traduction.

Était-ce utile?

La solution

Cela dépend de ce que vous entendez par "vraiment conforme aux normes". Cependant, la réponse courte est "c’est une bonne idée de s’assurer que toutes les fonctions disposent d’un prototype avant de les utiliser".

Une réponse plus qualifiée indique que si la fonction accepte des arguments variables (notamment la famille de fonctions printf () ), un prototype doit alors être dans la portée pour être strictement conforme aux normes. C'est le cas de C89 (de ANSI) et de C90 (de ISO; identique à C89 à l'exception de la numérotation de section). Cependant, à part les fonctions 'varargs', les fonctions renvoyant un int ne doivent pas être déclarées et les fonctions renvoyant autre chose qu'un int nécessitent une déclaration indiquant le type de retour, mais vous n'avez pas besoin du prototype pour la liste d'arguments.

Notez cependant que si la fonction accepte des arguments sujets à des "promotions normales" en l'absence de prototypes (par exemple, une fonction prenant un char ou un court - tous deux convertis en int ; plus sérieusement, peut-être une fonction prenant un float au lieu d'un double , puis un prototype est nécessaire. La norme était laxiste à ce sujet pour permettre à l’ancien code C de se compiler sous des compilateurs conformes standard; L'ancien code n'était pas écrit dans le but de s'assurer que les fonctions étaient déclarées avant d'être utilisées - et par définition, l'ancien code n'utilisait pas de prototypes, car ils n'étaient pas disponibles en C tant qu'il n'existait pas de norme.

C99 n'autorise pas 'implicit int' ... cela signifie à la fois des cas étranges comme ' static a; ' (un int par défaut) et des déclarations de fonction implicites. Celles-ci sont mentionnées (avec environ 50 autres modifications majeures) dans l'avant-propos de l'ISO / CEI 9899: 1999, qui compare cette norme aux versions précédentes:

  
      
  • supprime int implicite
      …
  •   
  • supprime la déclaration de fonction implicite
  •   

Dans ISO / CEI 9899: 1990, le § 6.3.2.2 Appels de fonction a déclaré:

  

Si l'expression qui précède la liste des arguments entre parenthèses dans un appel de fonction consiste   uniquement d'un identifiant, et si aucune déclaration n'est visible pour cet identifiant, l'identifiant est implicitement   déclarée exactement comme si, dans le bloc le plus à l'intérieur contenant l'appel de fonction, la déclaration:

extern int identifier();
     

est apparu. 38

     

38 C'est-à-dire un identifiant avec une portée de bloc déclaré comme ayant une liaison externe avec une fonction type sans   informations de paramètre et retour d'un int . Si en fait il n’est pas défini comme ayant le type “fonction   renvoyant int , "le comportement n'est pas défini.

Ce paragraphe manque dans la norme de 1999. Je n'ai pas (encore) suivi le changement de verbiage qui autorise statique a; dans C90 et ne l'autorise pas (nécessitant statique int a; ) dans C99.

Notez que si une fonction est statique, elle peut être définie avant son utilisation et ne doit pas nécessairement être précédée d'une déclaration. GCC peut être persuadé de perdre son temps si une fonction non statique est définie sans déclaration préalable ( -Wmissing-prototypes ).

Autres conseils

Un prototype est une déclaration de fonction qui spécifie les types de paramètres de la fonction.

Pre-ANSI C (le langage décrit par la première édition de Kernighan & Ritchie's "The C Programming Language" en 1978) n’avait pas de prototypes; il n'était pas possible pour une déclaration de fonction de décrire le nombre ou les types de paramètres. Il appartenait à l'appelant de transmettre le nombre et le type d'arguments corrects.

ANSI C a introduit les "prototypes", déclarations spécifiant les types de paramètres (fonctionnalité empruntée au début de C ++).

À partir de C89 / C90 (les normes ANSI et ISO décrivent le même langage), il est légal d'appeler une fonction sans déclaration visible. une déclaration implicite est fournie. Si la déclaration implicite est incompatible avec la définition proprement dite (par exemple, en appelant sqrt ("foo") , le comportement n'est pas défini. Ni cette déclaration implicite, ni une déclaration non prototype ne peut être compatible avec une fonction variadique, donc tout appel à une fonction variadique (comme printf ou scanf ) doit avoir un prototype visible.

C99 a abandonné les déclarations implicites. Tout appel à une fonction sans déclaration visible est une violation de contrainte nécessitant un diagnostic du compilateur. Mais il n'est toujours pas nécessaire que cette déclaration soit un prototype; il peut s'agir d'une déclaration de style ancien qui ne spécifie pas les types de paramètres.

C11 n’a apporté aucun changement significatif dans ce domaine.

Ainsi, même à partir de la norme ISO C 2011, les déclarations et définitions de fonction à l'ancien style (obsolètes depuis 1989) sont toujours autorisées dans du code conforme.

Pour toutes les versions de C remontant à 1989, pour des raisons de style, il y a très peu de raisons de ne pas utiliser de prototypes pour toutes les fonctions. Les déclarations et définitions de style ancien ne sont conservées que pour éviter de casser l'ancien code.

Non, les fonctions n’ont pas toujours besoin d’un prototype. La seule exigence est qu’une fonction soit " déclarée " avant de l'utiliser. Il existe deux manières de déclarer une fonction: écrire un prototype ou écrire la fonction elle-même (appelée "définition"). Une définition est toujours une déclaration, mais toutes les déclarations ne sont pas des définitions.

Oui, chaque fonction doit avoir un prototype, mais ce prototype peut figurer dans une déclaration distincte ou faire partie de la définition de la fonction. Les définitions de fonction écrites en C89 et supérieur ont naturellement des prototypes, mais si vous écrivez des choses dans le style classique K & R, voici ce qui suit:

main (argc, argv)

  int argc;
  char **argv;

{
  ...
}

alors la définition de la fonction n'a pas de prototype. Si vous écrivez un style ANSI C (C89), ainsi:

main (int argc, char **argv) { ... }

alors la définition de la fonction a un prototype.

Une astuce intéressante lors de l'écriture de nouvelles fonctions est de les écrire à l'envers avec main en bas. Ainsi, lorsque vous changez d'avis sur les arguments ou le type de retour de la fonction, vous n'avez pas besoin de réparer le prototype. La correction constante des prototypes et le traitement de tous les avertissements du compilateur lorsqu'ils sont périmés deviennent vraiment fastidieux.

Une fois que vos fonctions fonctionnent correctement, déplacez le code vers un module bien nommé et placez les prototypes dans un fichier .h du même nom. Cela fait gagner beaucoup de temps. La plus grande aide à la productivité que j'ai trouvée en 5 ans.

Au meilleur de mes connaissances (normes ANSI C89 / ISO C90), non. Je ne suis pas sûr de C99; Cependant, je m'attendrais à la même chose.

Note personnelle: je n'écris que des prototypes de fonctions lorsque ...

  1. Je dois (lorsque A () appelle B () et B () appelle A ()) ou
  2. J'exporte la fonction; sinon, cela semble superflu.
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top