Un forum pour les nouvelles technologies de l'information et de la communication.
 
AccueilDernières imagesRechercherS'enregistrerConnexion

Le Deal du moment : -17%
SSD interne Crucial SSD P3 1To NVME à ...
Voir le deal
49.99 €

 

 Le langage C Chapitre 10 : Les fonctions

Aller en bas 
AuteurMessage
Administrateur
Admin
Administrateur


Messages : 118
Date d'inscription : 01/11/2012

Le langage C Chapitre 10 : Les fonctions Empty
MessageSujet: Le langage C Chapitre 10 : Les fonctions   Le langage C Chapitre 10 : Les fonctions EmptyDim 11 Nov - 13:53

Chapitre 4 Les fonctions

Comme dans la plupart des langages, on peut en C découper un programme en
plusieurs fonctions. Une seule de ces fonctions existe obligatoirement ;
c'est la fonction principale appelée main.
Cette fonction principale peut, éventuellement, appeler une ou plusieurs
fonctions secondaires. De même, chaque fonction secondaire peut appeler
d'autres fonctions secondaires ou s'appeler elle-même (dans ce dernier cas, on
dit que la fonction est récursive).


4.1 Définition d'une fonction


La définition d'une fonction est la donnée du texte de son
algorithme, qu'on appelle corps de la fonction. Elle est de la forme


type nom-fonction (type-1 arg-1,...,type-n arg-n){[déclarations de variables locales ] liste d'instructions}

La première ligne de cette définition est l'en-tête
de la fonction. Dans cet en-tête, type désigne le type de la fonction, c'est-à-dire le
type de la valeur qu'elle retourne. Contrairement à d'autres langages, il n'y a
pas en C de notion de procédure ou de sous-programme. Une fonction qui ne
renvoie pas de valeur est une fonction dont le type est spécifié par le
mot-clef void. Les arguments de
la fonction sont appelés paramètres formels, par opposition aux paramètres
effectifs
qui sont les paramètres avec lesquels la fonction est
effectivement appelée. Les paramètres formels peuvent être de n'importe quel
type. Leurs identificateurs n'ont d'importance qu'à l'intérieur de la fonction.
Enfin, si la fonction ne possède pas de paramètres, on remplace la liste de
paramètres formels par le mot-clef void.

Le corps de la fonction débute éventuellement par des déclarations de
variables, qui sont locales à cette fonction. Il se termine par l'instruction
de retour à la fonction appelante
, return,
dont la syntaxe est


return(expression);


La valeur de expression est la valeur que retourne la fonction. Son
type doit être le même que celui qui a été spécifié dans l'en-tête de la
fonction. Si la fonction ne retourne pas de valeur (fonction de type void), sa définition s'achève par

return;

Plusieurs instructions return
peuvent apparaître dans une fonction. Le retour au programme appelant sera
alors provoqué par le premier return
rencontré lors de l'exécution. Voici quelques exemples de définitions de
fonctions :


int produit (int a, int b){ return(a*b);}int puissance (int a, int n){ if (n == 0) return(1); return(a * puissance(a, n-1));}void imprime_tab (int *tab, int nb_elements){ int i; for (i = 0; i < nb_elements; i++) printf("%d \t",tab); printf("\n"); return;}

4.2 Appel d'une fonction

L'appel d'une fonction se fait par l'expression

[i]nom-fonction
(para-1,para-2,...,para-n)


L'ordre et le type des paramètres effectifs de la fonction
doivent concorder avec ceux donnés dans l'en-tête de la fonction. Les
paramètres effectifs peuvent être des expressions. La virgule qui sépare deux
paramètres effectifs est un simple signe de ponctuation ; il ne s'agit pas
de l'opérateur virgule. Cela implique en particulier que l'ordre
d'évaluation des paramètres effectifs n'est pas assuré et dépend du
compilateur. Il est donc déconseillé, pour une fonction à plusieurs paramètres,
de faire figurer des opérateurs d'incrémentation ou de décrémentation (++ ou --)
dans les expressions définissant les paramètres effectifs (cf.
Chapitre 1).

4.3 Déclaration d'une fonction

Le C n'autorise pas les fonctions imbriquées. La définition
d'une fonction secondaire doit donc être placée soit avant, soit après la
fonction principale main.
Toutefois, il est indispensable que le compilateur ``connaisse'' la fonction au
moment où celle-ci est appelée. Si une fonction est définie après son premier
appel (en particulier si sa définition est placée après la fonction main), elle doit impérativement être
déclarée au préalable. Une fonction secondaire est déclarée par son prototype,
qui donne le type de la fonction et celui de ses paramètres, sous la
forme :

type nom-fonction(type-1,...,type-n);

Les fonctions secondaires peuvent être déclarées
indifféremment avant ou au début de la fonction main. Par exemple, on écrira


int puissance (int, int ); int puissance (int a, int n){ if (n == 0) return(1); return(a * puissance(a, n-1));} main(){ int a = 2, b = 5; printf("%d\n", puissance(a,b));}

Même si la déclaration
est parfois facultative (par exemple quand les fonctions sont définies avant la
fonction main et dans le bon
ordre), elle seule permet au compilateur de vérifier que le nombre et le type
des paramètres utilisés dans la définition concordent bien avec le protype. De
plus, la présence d'une déclaration permet au compilateur de mettre en place
d'éventuelles conversions des paramètres effectifs, lorsque la fonction est
appelée avec des paramètres dont les types ne correspondent pas aux types
indiqués dans le prototype. Ainsi les fichiers d'extension .h de la librairie standard (fichiers
headers) contiennent notamment les prototypes des fonctions de la librairie
standard. Par exemple, on trouve dans le fichier math.h le prototype de la fonction pow (élévation à la puissance) :


extern double pow(double , double );

La directive au préprocesseur


#include

permet au préprocesseur d'inclure la déclaration de la
fonction pow dans le fichier
source. Ainsi, si cette fonction est appelée avec des paramètres de type int, ces paramètres seront convertis en double lors de la compilation.

Par contre, en l'absence de directive au préprocesseur, le compilateur ne peut
effectuer la conversion de type. Dans ce cas, l'appel à la fonction pow avec des paramètres de type int peut produire un résultat faux !

4.4 Durée de vie des variables


Les variables manipulées dans
un programme C ne sont pas toutes traitées de la même manière. En
particulier, elles n'ont pas toutes la même durée de vie. On distingue
deux catégories de variables.

Les variables permanentes (ou statiques)

Une variable permanente occupe
un emplacement en mémoire qui reste le même durant toute l'exécution du
programme. Cet emplacement est alloué une fois pour toutes lors de la
compilation. La partie de la mémoire contenant les variables permanentes est
appelée segment de données. Par défaut, les variables permanentes sont
initialisées à zéro par le compilateur. Elles sont caractérisées par le
mot-clef static.

Les variables temporaires

Les variables temporaires se voient allouer un emplacement
en mémoire de façon dynamique lors de l'exécution du programme. Elles ne sont
pas initialisées par défaut. Leur emplacement en mémoire est libéré par exemple
à la fin de l'exécution d'une fonction secondaire.

Par défaut, les variables temporaires sont situées dans la partie de la mémoire
appelée segment de pile. Dans ce cas, la variable est dite automatique.
Le spécificateur de type correspondant, auto,
est rarement utilisé puisqu'il ne s'applique qu'aux variables temporaires qui
sont automatiques par défaut.

Une variable temporaire peut également être placée dans un registre de la
machine. Un registre est une zone mémoire sur laquelle sont effectuées les
opérations machine. Il est donc beaucoup plus rapide d'accéder à un registre
qu'à toute autre partie de la mémoire. On peut demander au compilateur de
ranger une variable très utilisée dans un registre, à l'aide de l'attribut de
type register. Le nombre de
registres étant limité, cette requête ne sera satisfaite que s'il reste des
registres disponibles. Cette technique permettant d'accélérer les programmes a
aujourd'hui perdu tout son intérêt. Grâce aux performances des optimiseurs de
code intégrés au compilateur (cf. options -O de gcc),
il est maintenant plus efficace de compiler un programme avec une option
d'optimisation que de placer certaines variables dans des registres.
La durée de vie des variables est liée à leur portée, c'est-à-dire à
la portion du programme dans laquelle elles sont définies.

4.4.1 Variables globales

On appelle variable globale une variable déclarée
en dehors de toute fonction. Une variable globale est connue du compilateur
dans toute la portion de code qui suit sa déclaration. Les variables globales
sont systématiquement permanentes. Dans le programme suivant, n est une variable globale :

int n;void fonction(); void fonction(){ n++; printf("appel numero %d\n",n); return;} main(){ int i; for (i = 0; i < 5; i++) fonction();}

La variable n
est initialisée à zéro par le compilateur et il s'agit d'une variable
permanente. En effet, le programme affiche

appel numero 1appel numero 2appel numero 3appel numero 4appel numero 5

4.4.2 Variables locales

On appelle variable locale une variable déclarée à
l'intérieur d'une fonction (ou d'un bloc d'instructions) du programme. Par
défaut, les variables locales sont temporaires. Quand une fonction est appelée,
elle place ses variables locales dans la pile. A la sortie de la fonction, les
variables locales sont dépilées et donc perdues.

Les variables locales n'ont en particulier aucun lien avec des variables
globales de même nom. Par exemple, le programme suivant

int n = 10;void fonction(); void fonction(){ int n = 0; n++; printf("appel numero %d\n",n); return;} main(){ int i; for (i = 0; i < 5; i++) fonction();}

affiche

appel numero 1appel numero 1appel numero 1appel numero 1appel numero 1

Les variables locales à une fonction ont une durée de vie
limitée à une seule exécution de cette fonction. Leurs valeurs ne sont pas
conservées d'un appel au suivant.
Il est toutefois possible de créer une variable locale de
classe statique en faisant précéder sa déclaration du mot-clef static :

static type nom-de-variable;

Une telle variable reste locale à la fonction dans laquelle
elle est déclarée, mais sa valeur est conservée d'un appel au suivant. Elle est
également initialisée à zéro à la compilation. Par exemple, dans le programme
suivant, n est une variable
locale à la fonction secondaire fonction,
mais de classe statique.

int n = 10;void fonction(); void fonction(){ static int n; n++; printf("appel numero %d\n",n); return;} main(){ int i; for (i = 0; i < 5; i++) fonction();}

Ce programme affiche

appel numero 1appel numero 2appel numero 3appel numero 4appel numero 5

On voit que la variable locale n est de classe statique (elle est initialisée à zéro, et
sa valeur est conservée d'un appel à l'autre de la fonction). Par contre, il
s'agit bien d'une variable locale, qui n'a aucun lien avec la variable globale
du même nom.

4.5 Transmission des paramètres d'une
fonction


Les paramètres d'une fonction sont traités de la même
manière que les variables locales de classe automatique : lors de l'appel
de la fonction, les paramètres effectifs sont copiés dans le segment de pile.
La fonction travaille alors uniquement sur cette copie. Cette copie disparaît
lors du retour au programme appelant. Cela implique en particulier que, si la
fonction modifie la valeur d'un de ses paramètres, seule la copie sera
modifiée ; la variable du programme appelant, elle, ne sera pas modifiée.
On dit que les paramètres d'une fonction sont transmis par valeurs.
Par exemple, le programme suivant

void echange (int, int ); void echange (int a, int b){ int t; printf("debut fonction :\n a = %d \t b = %d\n",a,b); t = a; a = b; b = t; printf("fin fonction :\n a = %d \t b = %d\n",a,b); return;} main(){ int a = 2, b = 5; printf("debut programme principal :\n a = %d \t b = %d\n",a,b); echange(a,b); printf("fin programme principal :\n a = %d \t b = %d\n",a,b);}

imprime


debut programme principal : a = 2 b = 5debut fonction : a = 2 b = 5fin fonction : a = 5 b = 2fin programme principal : a = 2 b = 5

Pour qu'une fonction modifie la valeur d'un de ses
arguments, il faut qu'elle ait pour paramètre l'adresse de cet objet et non sa
valeur. Par exemple, pour échanger les valeurs de deux variables, il faut
écrire :


void echange (int *, int *); void echange (int *adr_a, int *adr_b){ int t; t = *adr_a; *adr_a = *adr_b; *adr_b = t; return;} main(){ int a = 2, b = 5; printf("debut programme principal :\n a = %d \t b = %d\n",a,b); echange(&a,&b); printf("fin programme principal :\n a = %d \t b = %d\n",a,b);}

Rappelons qu'un tableau est un pointeur (sur le premier
élément du tableau). Lorsqu'un tableau est transmis comme paramètre à une
fonction secondaire, ses éléments sont donc modifiés par la fonction. Par
exemple, le programme


#include void init (int *, int ); void init (int *tab, int n){ int i; for (i = 0; i < n; i++) tab = i; return;} main(){ int i, n = 5; int *tab; tab = (int*)malloc(n * sizeof(int)); init(tab,n);}

initialise les éléments du tableau tab.

4.6 Les qualificateurs de type const
et volatile


Les qualificateurs de type const et volatile
permettent de réduire les possibilités de modifier une variable.

const

Une variable dont le type est
qualifié par const ne peut pas
être modifiée. Ce qualificateur est utilisé pour se protéger d'une erreur de
programmation. On l'emploie principalement pour qualifier le type des
paramètres d'une fonction afin d'éviter de les modifier involontairement.

volatile


Une variable dont le type est qualifié par volatile ne peut pas être impliquée dans
les optimisations effectuées par le compilateur. On utilise ce qualificateur
pour les variables susceptibles d'être modifiées par une action extérieure au
programme.
Les qualificateurs de type se placent juste avant le type de la variable, par
exemple

const char c;

désigne un caractère non modifiable. Ils doivent toutefois
être utilisés avec précaution avec les pointeurs. En effet,


const char *p;

définit un pointeur sur un caractère constant, tandis que

char * const p;

définit un pointeur constant sur un caractère.

4.7 La fonction main

La fonction principale main est une fonction comme les autres. Nous
avons jusqu'à présent considéré qu'elle était de type void, ce qui est toléré par le
compilateur. Toutefois l'écriture

main()

provoque un message d'avertissement lorsqu'on utilise
l'option -Wall de gcc :

% gcc -Wall prog.cprog.c:5: warning: return-type defaults to `int'prog.c: In function `main':prog.c:11: warning: control reaches end of non-void function

En fait, la fonction main
est de type int. Elle doit
retourner un entier dont la valeur est transmise à l'environnement d'exécution.
Cet entier indique si le programme s'est ou non déroulé sans erreur. La valeur
de retour 0 correspond à une terminaison correcte, toute valeur de retour
non nulle correspond à une terminaison sur une erreur. On peut utiliser comme
valeur de retour les deux constantes symboliques EXIT_SUCCESS (égale à 0) et EXIT_FAILURE (égale à 1) définies dans stdlib.h. L'instruction return([i]statut
);
dans la fonction main, où statut est un
entier spécifiant le type de terminaison du programme, peut être remplacée par
un appel à la fonction exit de
la librairie standard (stdlib.h).
La fonction exit, de prototype

void exit(int statut);

provoque une terminaison normale du programme en notifiant
un succès ou un échec selon la valeur de l'entier statut.

Lorsqu'elle est utilisée sans arguments, la fonction main a donc pour prototype

int main(void);

On s'attachera désormais dans les programmes à respecter ce
prototype et à spécifier les valeurs de retour de main.

La fonction main peut également
posséder des paramètres formels. En effet, un programme C peut recevoir
une liste d'arguments au lancement de son exécution. La ligne de commande qui
sert à lancer le programme est, dans ce cas, composée du nom du fichier
exécutable suivi par des paramètres. La fonction main reçoit tous ces éléments de la part de l'interpréteur
de commandes. En fait, la fonction main
possède deux paramètres formels, appelés par convention argc (argument count) et argv (argument vector). argc est une variable de type int dont la valeur est égale au nombre de
mots composant la ligne de commande (y compris le nom de l'exécutable). Elle
est donc égale au nombre de paramètres effectifs de la fonction + 1. argv est un tableau de chaînes de
caractères correspondant chacune à un mot de la ligne de commande. Le premier
élément argv[0] contient donc le
nom de la commande (du fichier exécutable), le second argv[1] contient le premier paramètre....
Le second prototype valide de la fonction main
est donc

int main ( int argc, char *argv[]);

Ainsi, le programme suivant calcule le produit de deux
entiers, entrés en arguments de l'exécutable :

#include #include int main(int argc, char *argv[]){ int a, b; if (argc != 3) { printf("\nErreur : nombre invalide d'arguments"); printf("\nUsage: %s int int\n",argv[0]); return(EXIT_FAILURE); } a = atoi(argv[1]); b = atoi(argv[2]); printf("\nLe produit de %d par %d vaut : %d\n", a, b, a * b); return(EXIT_SUCCESS);}
On lance donc l'exécutable avec deux paramètres entiers, par
exemple,

a.out 12 8

Ici, argv
sera un tableau de 3 chaînes de caractères argv[0],
argv[1] et argv[2] qui, dans notre exemple, valent
respectivement "a.out",
"12" et "8". Enfin, la fonction de la
librairie standard atoi(),
déclarée dans stdlib.h, prend en
argument une chaîne de caractères et retourne l'entier dont elle est l'écriture
décimale.

4.8 Pointeur sur une fonction

Il est parfois utile de passer une fonction comme paramètre
d'une autre fonction. Cette procédure permet en particulier d'utiliser une même
fonction pour différents usages. Pour cela, on utilise un mécanisme de
pointeur. Un pointeur sur une fonction correspond à l'adresse du début du code
de la fonction. Un pointeur sur une fonction ayant pour prototype
type fonction(type_1,...,type_n);
est de type
type (*)(type_1,...,type_n);
Ainsi, une fonction operateur_binaire
prenant pour paramètres deux entiers et une fonction de type int, qui prend elle-même deux entiers en
paramètres, sera définie par :


int operateur_binaire(int a, int b, int (*f)(int, int))

Sa déclaration est donnée par

int operateur_binaire(int, int, int(*)(int, int));

Pour appeler la fonction operateur_binaire,
on utilisera comme troisième paramètre effectif l'identificateur de la fonction
utilisée, par exemple, si somme
est une fonction de prototype

int somme(int, int);

on appelle la fonction operateur_binaire
pour la fonction somme par
l'expression

operateur_binaire(a,b,somme)

Notons qu'on n'utilise pas la notation &somme comme paramètre effectif de operateur_binaire.

Pour appeler la fonction passée en paramètre dans le corps de la fonction operateur_binaire, on écrit (*f)(a, b). Par exemple

int operateur_binaire(int a, int b, int (*f)(int, int)){ return((*f)(a,b));}

Ainsi, le programme suivant prend comme arguments deux entiers
séparés par la chaîne de caractères plus
ou fois, et retourne la somme ou
le produit des deux entiers.

#include #include #include void usage(char *);int somme(int, int);int produit(int, int);int operateur_binaire(int, int, int(*)(int, int)); void usage(char *cmd){ printf("\nUsage: %s int [plus|fois] int\n",cmd); return;} int somme(int a, int b){ return(a + b);} int produit(int a, int b){ return(a * b);} int operateur_binaire(int a, int b, int (*f)(int, int)){ return((*f)(a,b));} int main(int argc, char *argv[]){ int a, b; if (argc != 4) { printf("\nErreur : nombre invalide d'arguments"); usage(argv[0]); return(EXIT_FAILURE); } a = atoi(argv[1]); b = atoi(argv[3]); if (!strcmp(argv[2], "plus")) { printf("%d\n",operateur_binaire(a,b,somme)); return(EXIT_SUCCESS); } if (!strcmp(argv[2], "fois")) { printf("%d\n",operateur_binaire(a,b,produit)); return(EXIT_SUCCESS); } else { printf("\nErreur : argument(s) invalide(s)"); usage(argv[0]); return(EXIT_FAILURE); }}

Les pointeurs sur les fonctions sont
notamment utilisés dans la fonction de tri des éléments d'un tableau qsort et dans la recherche d'un élément
dans un tableau bsearch. Ces
deux fonctions sont définies dans la libriarie standard (stdlib.h).

Le prototype de la fonction de tri (algorithme quicksort) est


void qsort(void *tableau, size_t nb_elements, size_t taille_elements, int(*comp)(const void *, const void *));

Elle permet de trier les nb_elements
premiers éléments du tableau tableau.
Le paramètre taille_elements
donne la taille des éléments du tableau. Le type size_t utilisé ici est un type prédéfini dans stddef.h. Il correspond au type du
résultat de l'évaluation de sizeof.
Il s'agit du plus grand type entier non signé. La fonction qsort est paramétrée par la fonction de
comparaison utilisée de prototype :


int comp(void *a, void *b);

Les deux paramètres a
et b de la fonction comp sont des pointeurs génériques de type
void *. Ils correspondent à des
adresses d'objets dont le type n'est pas déterminé. Cette fonction de
comparaison retourne un entier qui vaut 0 si les deux objets pointés par a et b
sont égaux et qui prend une valeur strictement négative (resp. positive) si
l'objet pointé par a est
strictement inférieur (resp. supérieur) à celui pointé par b.

Par exemple, la fonction suivante comparant deux chaînes de caractères peut
être utilisée comme paramètre de qsort :



int comp_str(char **, char **); int comp_str(char **s1, char **s2){ return(strcmp(*s1,*s2));}

Le programme suivant donne un exemple de l'utilisation de la
fonction de tri qsort pour trier
les éléments d'un tableau d'entiers, et d'un tableau de chaînes de caractères.


#include #include #include #define NB_ELEMENTS 10 void imprime_tab1(int*, int);void imprime_tab2(char**, int);int comp_int(int *, int *);int comp_str(char **, char **); void imprime_tab1(int *tab, int nb){ int i; printf("\n"); for (i = 0; i < nb; i++) printf("%d \t",tab); printf("\n"); return;} void imprime_tab2(char **tab, int nb){ int i; printf("\n"); for (i = 0; i < nb; i++) printf("%s \t",tab[i]); printf("\n"); return;} int comp_int(int *a, int *b){ return(*a - *b);}int comp_str(char **s1, char **s2){ return(strcmp(*s1,*s2));} int main(){ int *tab1; char *tab2[NB_ELEMENTS] = {"toto", "Auto", "auto", "titi", "a", "b","z", "i , "o","d"}; int i; tab1 = (int*)malloc(NB_ELEMENTS * sizeof(int)); for (i = 0 ; i < NB_ELEMENTS; i++) tab1[i] = random() % 1000; imprime_tab1(tab1, NB_ELEMENTS); qsort(tab1, NB_ELEMENTS, sizeof(int), comp_int); imprime_tab1(tab1, NB_ELEMENTS); /************************/ imprime_tab2(tab2, NB_ELEMENTS); qsort(tab2, NB_ELEMENTS, sizeof(tab2[0]), comp_str); imprime_tab2(tab2, NB_ELEMENTS); return(EXIT_SUCCESS);}

La librairie standard dispose également d'une fonction de
recherche d'un élément dans un tableau [i]trié
, ayant le prototype
suivant :


void *bsearch((const void *clef, const void *tab, size_t nb_elements,size_t taille_elements, int(*comp)(const void *, const void *)));

Cette fonction recherche dans le tableau trié tab un élément qui soit égal à l'élément
d'adresse clef. Les autres
paramètres sont identiques à ceux de la fonction qsort. S'il existe dans le tableau tab un élément égal à celui pointé par clef, la fonction bsearch retourne son adresse (de type void *). Sinon, elle retourne le pointeur NULL.

Ainsi, le programme suivant prend en argument une chaîne de caractères et
détermine si elle figure dans un tableau de chaînes de caractères prédéfini,
sans différencier minuscules et majuscules. Rappelons que bsearch ne s'applique qu'aux tableaux
triés ; il faut donc appliquer au préalable la fonction de tri qsort.


#include #include #include #include #define NB_ELEMENTS 4 int comp_str_maj(char **, char **); int comp_str_maj(char **s1, char **s2){ int i; char *chaine1, *chaine2; chaine1 = (char*)malloc(strlen(*s1) * sizeof(char)); chaine2 = (char*)malloc(strlen(*s2) * sizeof(char)); for (i = 0; i < strlen(*s1); i++) chaine1[i] = tolower((*s1)[i]); for (i = 0; i < strlen(*s2); i++) chaine2[i] = tolower((*s2)[i]); return(strcmp(chaine1,chaine2));} int main(int argc, char *argv[]){ char *tab[NB_ELEMENTS] = {"TOTO", "Auto", "auto", "titi"}; char **res; qsort(tab, NB_ELEMENTS, sizeof(tab[0]), comp_str_maj); if ((res = bsearch(&argv[1],tab,NB_ELEMENTS,sizeof(tab[0]),comp_str_maj)) ==\NULL) printf("\nLe tableau ne contient pas l'element %s\n",argv[1]); else printf("\nLe tableau contient l'element %s sous la forme %s\n",argv[1], \ *res); return(EXIT_SUCCESS);}

4.9 Fonctions avec un nombre variable de
paramètres


Il est possible en C de définir des fonctions qui ont
un nombre variable de paramètres. En pratique, il existe souvent des méthodes
plus simples pour gérer ce type de problème : toutefois, cette
fonctionnalité est indispensable dans certains cas, notamment pour les
fonctions printf et scanf.

Une fonction possédant un nombre variable de paramètre doit posséder au moins
un paramètre formel fixe. La notation ...(obligatoirement à la fin de la liste
des paramètres d'une fonction) spécifie que la fonction possède un nombre
quelconque de paramètres (éventuellement de types différents) en plus des
paramètres formels fixes. Ainsi, une fonction ayant pour prototype


int f(int a, char c, ...);

prend comme paramètre un entier, un caractère et un nombre
quelconque d'autres paramètres. De même le prototype de la fonction printf est


int printf(char *format, ...);

puisque printf
a pour argument une chaîne de caractères spécifiant le format des données à
imprimer, et un nombre quelconque d'autres arguments qui peuvent être de types
différents.

Un appel à une fonction ayant un nombre variable de paramètres s'effectue comme
un appel à n'importe quelle autre fonction.

Pour accéder à la liste des paramètres de l'appel, on utilise les macros
définies dans le fichier en-tête stdarg.h
de la librairie standard. Il faut tout d'abord déclarer dans le corps de la
fonction une variable pointant sur la liste des paramètres de l'appel ;
cette variable a pour type va_list.
Par exemple,


va_list liste_parametres;

Cette variable est tout d'abord initialisée à l'aide de la
macro va_start, dont la syntaxe
est


va_start(liste_parametres, dernier_parametre);

où dernier_parametre
désigne l'identificateur du dernier paramètre formel fixe de la fonction. Après
traitement des paramètres, on libère la liste à l'aide de la va_end :


va_end(liste_parametres);

On accède aux différents paramètres de liste par la macro va_arg qui retourne le paramètre suivant
de la liste:


va_arg(liste_parametres, type)

où type est
le type supposé du paramètre auquel on accède.

Notons que l'utilisateur doit lui-même gérer le nombre de paramètres de la
liste. Pour cela, on utilise généralement un paramètre formel qui correspond au
nombre de paramètres de la liste, ou une valeur particulière qui indique la fin
de la liste.

Cette méthode est utilisée dans le programme suivant, où la fonction add effectue la somme de ses paramètres en
nombre quelconque.


#include #include #include int add(int,...); int add(int nb,...){ int res = 0; int i; va_list liste_parametres; va_start(liste_parametres, nb); for (i = 0; i < nb; i++) res += va_arg(liste_parametres, int); va_end(liste_parametres); return(res);} int main(void){ printf("\n %d", add(4,10,2,8,5)); printf("\n %d\n", add(6,10,15,5,2,8,10)); return(EXIT_SUCCESS);}
Revenir en haut Aller en bas
https://jeveux.fra.co
 
Le langage C Chapitre 10 : Les fonctions
Revenir en haut 
Page 1 sur 1
 Sujets similaires
-
» Le langage C Chapitre 9: Les pointeurs
» Le langage C Chapitre 2 : La compilation
» Le langage C Chapitre 1 : Historique
» Le langage C Chapitre 8: Les types composés
» Le langage C Chapitre 6 : Constantes et opérateurs

Permission de ce forum:Vous ne pouvez pas répondre aux sujets dans ce forum
Forum Je veux :: Les logiciels PC: Windows, Linux, Jeux... :: Programmation, Web & Ftp-
Sauter vers:  

Vous êtes le visiteur N°

En s’abonnant aux flux RSS vous allez recevoir tous les articles de Forum " Je veux " dans votre lecteur de flux RSS quasiment instantanément après leurs publication.
L’intérêt est que vous n’êtes plus obliger de vous rendre sur le site pour voir s’il y a du nouveau.Pour nous suivre par RSS, cliquer sur le bouton RSS suivant:

Forum je veux


Forum Je Veux by A. Houssam


Annuaire gratuit Annuaire sites webMaroc index: Annuaire de sites marocains Annuaire sites webAvis consommateurs
Forum "Je veux" sur Facebook et Google
Chaque sujet enflame notre créativité