Sunteți pe pagina 1din 8

Programmation C/Fonctions et procdures

Programmation C/Fonctions et procdures


Dfinition
Le code suivant dfinit une fonction fonction renvoyant une valeur de type type_retour et prenant N arguments, par1 de type type1, par2 de type type2, etc. type_retour fonction(type1 par1, type2 par2, /* ..., */ typeN parN) { /* Dclarations de variables ... */ /* Instructions ... */ } L'excution d'une fonction se termine soit lorsque l'accolade fermante est atteinte, soit lorsque le mot clef return est rencontr. La valeur renvoye par une fonction est donne comme paramtre return. Une procdure est une fonction renvoyant void, dans ce cas return est appel sans paramtre. Les passages des arguments aux fonctions se font toujours par valeur. Si on veut modifier la valeur d'un argument pass en paramtre une fonction, en dehors de cette mme fonction, il faut utiliser des pointeurs.

Dclaration par prototype


Le prototype d'une fonction correspond simplement son en-tte (tout ce qui prcde la premire accolade ouvrante). C'est--dire son nom, son type de retour et les types des diffrents paramtres. Cela permet au compilateur de vrifier que la fonction est appele avec le bon nombre de paramtres et surtout avec les bons types. La ligne suivante dclare la fonction fonction, mais sans la dfinir : type_retour nom_fonction(type1, type2, /* ..., */ typeN); noter que les noms des paramtres peuvent tre omis et que la dclaration doit se terminer par un point-virgule (;), sans quoi vous pourrez vous attendre une cascade d'erreurs.

Absence des paramtres


Avant la normalisation par l'ANSI, il tait possible de faire une dclaration partielle d'une fonction, en spcifiant son type de retour, mais pas ses paramtres: int f(); Cette dclaration ne dit rien sur les ventuels paramtes de la fonction f, sur leur nombre ou leur type, au contraire de : int g(void); qui prcise que la fonction g ne prend aucun argument. Cette dclaration partielle laissait au compilateur le soin de complter la dclaration lors de l'appel de la fonction, ou de sa dfinition. On perd donc un grand intrt des prototypes. Mais cause de l'immense quantit de code existant qui se reposait sur ce comportement, l'ANSI (puis le WG14) n'ont pas interdit de tels programmes, mais ont dclar ds le C90 que cette construction est obsolte.

Programmation C/Fonctions et procdures

valuation des arguments


La norme du langage ne spcifie pas l'ordre d'valuation des arguments. Il faut donc faire particulirement attention aux effets de bords. Ce code contient une erreur volontaire ! #include <stdio.h> int somme(int a, int b) {return a + b; } int main(void) { int i = 0; printf("%d\n", return 0; }

somme(++i, i)

);

Voici un premier exemple. Lors de l'appel de la fonction somme, si l'expression ++i est value avant l'expression i, alors le programme affichera 2. Si, au contraire, c'est l'expression i qui est value avant l'expression ++i, alors le programme affichera 1. Ce code contient une erreur volontaire ! #include <stdio.h> int fonction(int, int); int g(void); int h(void); int test(void) { return fonction(g(), h()); } Dans cet autre exemple, les expressions g() et h() pouvant tre values dans n'importe quel ordre, on ne peut pas savoir laquelle des fonctions g et h sera appele en premier. Si l'appel de ces fonctions provoque des effets de bord (affichage de messages, modification de variables globales...), alors le comportement du programme est imprvisible. Pour pallier ce problme, il faut imposer l'ordre d'appel: #include <stdio.h> int fonction(int, int); int g(void); int h(void); int test(void) { int a,b; a = g(); b = h(); return fonction(a, b);

Programmation C/Fonctions et procdures }

Nombre variable d'arguments


Une fonctionnalit assez utile est d'avoir une fonction avec un nombre variable d'arguments, comme la fameuse fonction printf(). Pour cela, il suffit de dclarer le prototype de la fonction de la manire suivante :

Dclaration
#include <stdarg.h> void ma_fonction(type1 arg1, type2 arg2, ...) { } Dans l'exemple ci-dessus, les points de suspension ne sont pas un abus d'criture, mais bel et bien une notation C pour indiquer que la fonction accepte d'autres arguments. L'exemple est limit deux arguments, mais il est bien sr possible d'en spcifier autant qu'on veut. C'est dans l'unique but de ne pas rendre ambige la dclaration, qu'aucun abus d'criture n'a t employ. L'inclusion de l'en-tte <stdarg.h> n'est ncessaire que pour traiter les arguments l'intrieur de la fonction. La premire remarque que l'on peut faire est qu'une fonction nombre variable d'arguments contient au moins un paramtre fixe. En effet la dclaration suivante est invalide : Ce code contient une erreur volontaire ! void ma_fonction(...);

Accs aux arguments


Pour accder aux arguments situs aprs le dernier argument fixe, il faut utiliser certaines fonctions (ou plutt macros) de l'en-tte <stdarg.h> : void va_start (va_list ap, last); type va_arg (va_list ap, type); void va_end (va_list ap); va_list est un type opaque dont on n'a pas se soucier. On commence par l'initialiser avec va_start. Le paramtre last doit correspondre au nom du dernier argument fixe de la fonction, ou alors tout bon compilateur retournera au moins un avertissement. Vient ensuite la collecte minutieuse des arguments. Il faut bien comprendre qu' ce stade, le langage n'offre aucun moyen de savoir comment sont structures les donnes (c'est dire leur type). Il faut absolument dfinir une convention, laisse l'imagination du programmeur, pour pouvoir extraire les donnes correctement. Qui plus est, il faut tre extrmement vigilant lors de la rcupration des paramtres, cause de la promotion des types entiers ou rels. En effet, les entiers sont systmatiquement promus en int, sauf si la taille du type est plus grande, auquel cas le type est inchang. Pour les rels, le type float est promu en double, alors que le type long double est inchang. C'est pourquoi ce genre d'instruction n'a aucun sens dans une fonction nombre variable d'arguments : Ce code contient une erreur volontaire ! char caractere = va_arg(list, char); Il faut obligatoirement rcuprer un entier de type char, comme tant un entier de type int.

Programmation C/Fonctions et procdures

Exemple de convention
Un bon exemple de convention est la fonction printf() elle mme. Elle utilise un spcificateur de format qui renseigne la fois le nombre d'arguments qu'on s'attend trouver mais aussi le type de chacun. D'un autre cot, analyser un spcificateur de format est relativement rbarbatif, et on n'a pas toujours besoin d'une artillerie aussi lourde. Une autre faon de faire, relativement rpandue, est de ne passer que des couples (type, objet), o type correspond un code reprsentant un type (une numration par exemple) et objet le contenu de l'objet lui-mme (int, pointeur, double, etc.). On utilise alors un code spcial (gnralement 0) pour indiquer la fin des arguments, ou alors un des paramtres pour indiquer combien il y en a. Un petit exemple complet : #include <stdio.h> #include <stdarg.h> enum { TYPE_FIN, TYPE_ENTIER, TYPE_REEL, TYPE_CHAINE }; void affiche(FILE * out, ...) { va_list list; int type; va_start(list, out); while ((type = va_arg(list, int))) { switch (type) { case TYPE_ENTIER: fprintf(out, "%d", va_arg(list, int)); break; case TYPE_REEL: fprintf(out, "%g", va_arg(list, double)); break; case TYPE_CHAINE: fprintf(out, "%s", va_arg(list, char *)); break; } } fprintf(out, "\n"); va_end(list); } int main(int nb, char * argv[]) { affiche(stdout, TYPE_CHAINE, "Le code ascii de 'a' est ", TYPE_ENTIER, 'a', 0); affiche(stderr, TYPE_CHAINE, "2 * 3 / 5 = ", TYPE_REEL, 2. * 3 / 5, 0);

Programmation C/Fonctions et procdures return 0; } L'inconvnient de ce genre d'approche est de ne pas oublier le marqueur de fin. Dans les deux cas, il faut tre vigilant avec les conversions implicites, notamment dans le second cas. noter que la conversion (transtypage) explicite des types en une taille infrieure celle par dfaut (int pour les entiers ou double pour les rels) ne permet pas de contourner la promotion implicite. Mme crit de la sorte: affiche(stderr, TYPE_CHAINE, "2 * 3 / 5 = ", TYPE_REEL, (float) (2. * 3 / 5), 0); Le rsultat transmis au cinquime paramtre sera quand mme promu implicitement en type double.

Fonction inline
Il s'agit d'une extension ISO C99, qui l'origine vient du C++. Ce mot cl doit se placer avant le type de retour de la fonction. Il ne s'agit que d'une indication, le compilateur peut ne pas honorer la demande, notamment si la fonction est rcursive. Dans une certaine mesure, les fonctionnalits proposes par ce mot cl sont dj prises en charge par les instructions du prprocesseur. Beaucoup prfreront passer par une macro, essentiellement pour des raisons de compatibilit avec d'anciens compilateurs ne supportant pas ce mot cl, et quand bien mme l'utilisation de macro est souvent trs dlicat. Le mot cl inline permet de s'affranchir des nombreux dfauts des macros, et de rellement les utiliser comme une fonction normale, c'est dire surtout sans effets de bord. noter qu'il est prfrable de classifier les fonctions inline de manire statique. Dans le cas contraire, la fonction sera aussi dclare comme tant accessible de l'extrieur, et donc dfinie comme une fonction normale. En la dclarant static inline, un bon compilateur devrait supprimer toute trace de la fonction et seulement la mettre in extenso aux endroits o elle est utilise. Ceci permettrait la limite de dclarer la fonction dans un fichier en-tte, bien qu'il s'agisse d'une pratique assez rare et donc viter. Exemple de dclaration d'une fonction inline statique : static inline int max(int a, int b) { return (a > b) ? a : b; }

La fonction main
Nous allons revenir ici sur la fonction main, prsente dans chaque programme. Cette fonction est le point d'entre du programme. La norme dfinit deux prototypes, qui sont donc portables: int main(int argc, char * argv[]) { /* ... */ } int main(void) { /* ... */ } Le premier prototype est plus "gnral": il permet de rcuprer des paramtres au programme. Le deuxime existe pour des raisons de simplicit, quand on ne veut pas traiter ces arguments. Si ces deux prototypes sont portables, une implmentation peut nanmoins dfinir un autre prototype pour main, ou spcifier une autre fonction pour le dmarrage du programme. Cependant ces cas sont plus rares (et souvent spcifiques du C embarqu).

Programmation C/Fonctions et procdures

Paramtres de ligne de commande


La fonction main prend deux paramtres qui permettent d'accder aux paramtres passs au programme lors de son appel. Le premier, gnralement appel argc (argument count), est le nombre de paramtres qui ont t passs au programme. Le second, argv (argument vector), est la liste de ces paramtres. Les paramtres sont stocks sous forme de chane de caractres, argv est donc un tableau de chanes de caractres, ou un pointeur sur un pointeur sur char. argc correspond au nombre d'lments de ce tableau. La premire chane de caractres, dont l'adresse est dans argv[0], contient le nom du programme. Le premier paramtre est donc argv[1]. Le dernier lment du tableau, argv[argc], est un pointeur nul.

Valeur de retour
La fonction main retourne toujours une valeur de type entier. L'usage veut qu'on retourne 0 (ou EXIT_SUCCESS) si le programme s'est droul correctement, ou EXIT_FAILURE pour indiquer qu'il y a eu une erreur (Les macros EXIT_SUCCESS et EXIT_FAILURE tant dfinies dans l'en-tte <stdlib.h>). Il est possible par le programme appelant de rcuprer ce code de retour, et de l'interprter comme bon lui semble.

Exemple
Voici un petit programme trs simple qui affiche la liste des paramtres passs au programme lorsqu'il est appel: #include <stdio.h> int main(int argc, char * argv[]) { int i; for (i = 0; i < argc; i++) printf("paramtre %i : %s\n", i, argv[i]); return 0; } On effectue une boucle sur argv l'aide de argc. Enregistrez-le sous le nom params.c puis compilez-le (cc params.c -o params). Vous pouvez ensuite l'appeler ainsi: ./params hello world ! # sous Unix params.exe hello world ! # sous MS-DOS ou Windows Vous devriez voir en sortie quelque chose comme ceci (paramtre 0 varie selon le systme d'exploitation): paramtre paramtre paramtre paramtre 0 1 2 3 : : : : ./params hello world !

Programmation C/Fonctions et procdures

Fonctions en C pr-ANSI
Absence de prototype lors de l'appel d'une fonction
Avant la normalisation C89, on pouvait appeler une fonction sans disposer ni de sa dfinition, ni de sa dclaration. Dans ce cas, la fonction tait implicitement dclare comme retournant le type int, et prenant un nombre indtermin de paramtres. /* Aucune dclaration de g() n'est visible. */ void f(void) { g(); /* Dclaration implicite: extern int g() */ } cause de la grande quantit de code existant l'poque qui reposait sur ce comportement, le C90 a conserv cette possibilit. Cependant, elle a t retire de la norme C99, et est viter mme lorsqu'on travaille en C90. En effet, c'est plus qu'une bonne habitude de programmation de s'assurer que chaque fonction utilise dans un programme ait son prototype dclar avant qu'elle ne soit dfinie ou utilise. C'est d'autant plus indispensable lorsque les fonctions sont dfinies et utilises dans des fichiers diffrents.

Ancienne notation
titre anecdotique, ceci est la faon historique de dfinir une fonction, avant que le prototypage ne fut utilis. Cette notation est interdite depuis C90. type_retour fonction(par1, par2, ..., parN) type1 par1; type2 par2; ... typeN parN; { /* Dclarations de variables ... */ /* Instructions ... */ } Au lieu de dclarer les types l'intrieur mme de la fonction, ils sont simplement dcrits aprs la fonction et avant la premire accolade ouvrante. noter que type_retour pouvait tre omis, et dans ce cas valait par dfaut int.

Sources et contributeurs de l'article

Sources et contributeurs de l'article


Programmation C/ Fonctions et procdures Source: http://fr.wikibooks.org/w/index.php?oldid=239496 Contributeurs: Alveric, DavidL, Dhenry, Francois Trazzi, Guillaumito, Tados, Thierry46, Tpierron, 29 modifications anonymes

Source des images, licences et contributeurs


Image:Achtung.svg Source: http://fr.wikibooks.org/w/index.php?title=Fichier:Achtung.svg Licence: Public Domain Contributeurs: see below.

Licence
Creative Commons Attribution-Share Alike 3.0 Unported http:/ / creativecommons. org/ licenses/ by-sa/ 3. 0/

S-ar putea să vă placă și