Je suis un peu nouveau pour C (j'ai une expérience préalable en Java, C # et C++). En C, faut-il déclarer un prototype de fonction ou le code peut-il se compiler sans lui? Est-ce une bonne pratique de programmation de le faire? Ou cela dépend-il simplement du compilateur? (J'utilise Ubuntu 9.10 et j'utilise le compilateur GNU C, ou gcc, sous l'IDE Code :: Blocks))
Dans ANSI C (c'est-à-dire C89 ou C90), vous n'avez pas à déclarer un prototype de fonction; cependant, il est recommandé de les utiliser. La seule raison pour laquelle la norme vous permet de ne pas les utiliser est pour une compatibilité descendante avec un code très ancien.
Si vous n'avez pas de prototype et que vous appelez une fonction, le compilateur déduira un prototype des paramètres que vous transmettez à la fonction. Si vous déclarez la fonction plus tard dans la même unité de compilation, vous obtiendrez une erreur de compilation si la signature de la fonction est différente de ce que le compilateur a deviné.
Pire, si la fonction est dans une autre unité de compilation, il n'y a aucun moyen d'obtenir une erreur de compilation, car sans prototype, il n'y a aucun moyen de vérifier. Dans ce cas, si le compilateur se trompe, vous pourriez obtenir un comportement indéfini si l'appel de fonction pousse sur la pile des types différents de ceux attendus par la fonction.
La convention consiste à toujours déclarer un prototype dans un fichier d'en-tête portant le même nom que le fichier source contenant la fonction.
En C99 ou C11, la norme C requiert une déclaration de fonction dans la portée avant d'appeler une fonction. De nombreux compilateurs n'appliquent pas cette restriction dans la pratique à moins que vous ne les y obligiez.
Il n'est jamais nécessaire de déclarer un prototype pour une fonction en C, ni dans le "vieux" C (y compris C89/90) ni dans le nouveau C (C99 ). Cependant, il existe une différence significative entre C89/90 et C99 en ce qui concerne les déclarations de fonction.
En C89/90, il n'était pas du tout nécessaire de déclarer une fonction. Si la fonction n'est pas déclarée au moment de l'appel, le compilateur "devine" (infère) la déclaration implicitement à partir des types des arguments passés dans l'appel et suppose que le type de retour est int
.
Par exemple
int main() {
int i = foo(5);
/* No declaration for `foo`, no prototype for `foo`.
Will work in C89/90. Assumes `int foo(int)` */
return 0;
}
int foo(int i) {
return i;
}
En C99, chaque fonction que vous appelez doit être déclarée avant le point de l'appel. Cependant, il n'est toujours pas nécessaire de le déclarer avec un prototype spécifiquement. Une déclaration non prototype fonctionnera également. Cela signifie qu'en C99, la règle "implicite int
" ne fonctionne plus (pour les types de retour de fonction inférés, dans ce cas), mais les types de paramètre peuvent toujours être devinés à partir des types d'argument si la fonction est déclarée sans prototype.
L'exemple précédent ne se compilera pas en C99, car foo
n'est pas déclaré au moment de l'appel. Pourtant, vous pouvez ajouter une déclaration non prototype
int foo(); /* Declares `foo`, but still no prototype */
int main() {
int i = foo(5);
/* No prototype for `foo`, although return type is known.
Will work in C99. Assumes `int foo(int)` */
return 0;
}
...
et se retrouver avec un code C99 valide.
Néanmoins, il est toujours recommandé de déclarer un prototype pour la fonction avant de l'appeler.
ne note supplémentaire: J'ai dit plus haut qu'il n'est jamais nécessaire de déclarer un prototype de fonction. En fait, pour certaines fonctions, c'est une exigence. Pour appeler correctement une fonction variadique en C (printf
par exemple), la fonction doit être déclarée avec un prototype avant le point de l'appel. Sinon, le comportement n'est pas défini. Cela s'applique à la fois à C89/90 et C99.
ce n'est pas un must, si la fonction est définie avant son utilisation.
Ce n'est pas obligatoire, mais c'est une mauvaise pratique de ne pas utiliser de prototypes.
Avec les prototypes, le compilateur peut vérifier que vous appelez la fonction correctement (en utilisant le bon nombre et le bon type de paramètres).
Sans prototypes, il est possible d'avoir ceci:
// file1.c
void doit(double d)
{
....
}
int sum(int a, int b, int c)
{
return a + b + c;
}
et ça:
// file2.c
// In C, this is just a declaration and not a prototype
void doit();
int sum();
int main(int argc, char *argv[])
{
char idea[] = "use prototypes!";
// without the prototype, the compiler will pass a char *
// to a function that expects a double
doit(idea);
// and here without a prototype the compiler allows you to
// call a function that is expecting three argument with just
// one argument (in the calling function, args b and c will be
// random junk)
return sum(argc);
}
En C, si nous ne déclarons pas de prototype de fonction et utilisons la définition de fonction, il n'y a pas de problème et le programme compile et génère la sortie si le type de retour de la fonction est "entier". Dans toutes les autres conditions, l'erreur du compilateur apparaît. La raison en est que si nous appelons une fonction et ne déclarons pas un prototype de fonction, le compilateur génère un prototype qui renvoie un entier et recherche la définition de fonction similaire. si le prototype de fonction correspond alors il se compile avec succès. Si le type de retour n'est pas entier, les prototypes de fonctions ne correspondent pas et cela génère une erreur. Il est donc préférable de déclarer le prototype de fonction dans les fichiers d'en-tête.
C permet d'appeler des fonctions même si elles n'ont pas été déclarées auparavant, mais je vous recommande fortement de déclarer un prototype pour toutes les fonctions avant de les utiliser afin que le compilateur puisse vous sauver si vous utilisez les mauvais arguments.
Vous devez placer les déclarations de fonction dans le fichier d'en-tête (X.h) et la définition dans le fichier source (X.c). Ensuite, d'autres fichiers peuvent #include "X.h"
et appelez la fonction.
Le prototype de fonction n'est pas obligatoire selon le C99
la norme.
Il n'est pas absolument nécessaire de déclarer une fonction pour le code appelant à compiler. Il y a cependant des mises en garde. La fonction non déclarée est supposée renvoyer int
et le compilateur émettra d'abord des avertissements sur la fonction non déclarée, puis sur toute incompatibilité dans le type de retour et les types de paramètres.
Cela dit, il est évident que déclarer correctement des fonctions avec des prototypes est une bien meilleure pratique.