Aller au menu - Aller au contenu

[Plan du site] Vous êtes ici --- > Le Site du Zéro > Les tutoriels > Non-Officiels > Programmation > C > Les fonctions à nombre variable de paramètres > Lecture du tutoriel

Les fonctions à nombre variable de paramètres

Vous vous apprêtez à lire un tutoriel rédigé par un membre de ce site. Malgré tout le soin que ce membre a pu apporter au tutoriel, nous ne pouvons pas garantir que les informations contenues sur cette page sont exactes à 100%. Merci de garder cela en tête lorsque vous lirez cette page ;o)
Avatar
Auteur : 64th
Difficulté : Amateur (2 / 5)
Note : 19 / 20 (7 votes)
Visualisations : 5 337

Plus d'informations Plus d'informations
Bonjour à tous ! Soyez les bienvenus dans ce tutoriel sur les fonctions à nombre variable de paramètres.

Que sont les fonctions à nombre variable de paramètres ?
Eh bien ce sont des fonctions dont le nombre de paramètres n'est pas fixé dans leurs prototypes. Ainsi, on peut envoyer plus ou moins de paramètres à ces fonctions selon les circonstances :waw: . Et ces fonctions peuvent s'avérer très utiles lorsqu'on les connaît !

Les pré-requis sont : maîtriser le cours de C de M@teo21 jusqu'au chapitre 7 de la partie 2.
Derrière le nom barbare :diable: de 'fonction à nombre variable de paramètres' se cache une notion relativement simple, et très agréable à utiliser, rassurez-vous ^^ .


Sommaire du tutoriel :
Icône du chapitre

Prototype et appel de la fonction

Paramètres et prototype



Une fonction à nombre variable de paramètres (fonction variadic) contiendra deux types de paramètres :
Je vous présente donc ces paramètres sans plus tarder :magicien: .

Paramètres obligatoires



Les paramètres obligatoires sont comparables aux paramètres d'une fonction classique (en C) : ils devront toujours être appelés lors de l'utilisation de la fonction. Une fonction à nombre variable de paramètres doit contenir un paramètre obligatoire ou plus.

Paramètres optionnels



Les paramètres optionnels sont ceux qui font l'intérêt de la fonction... On pourra en envoyer 0, 1, 2, 40 ou 100 000 à la même fonction selon les circonstances. Du fait que l'on ne peut pas déclarer chaque paramètre dans le prototype, on déclarera seulement la présence de paramètres optionnels grâce à une "ellipse" :
Code : C
1
... // 3 points sans espace entre eux (ellipse)


Prototype de la fonction



Maintenant que l'on a vu les paramètres obligatoires et optionnels, rassemblons les deux :p !

On mettra d'abord le(s) paramètre(s) obligatoire(s), et ensuite les paramètres optionnels. C'est la seule particularité du prototype d'une fonction à nombre variable de paramètres. On a donc un prototype du type :
Code : C
1
2
type nomFonction(int parametreA, ...);
type nomFonction(int parametreA, int parametreB, ...);


Un petit problème vraiment gênant...



Voici ce qui va se passer lorsque votre ordinateur va obtenir les paramètres optionnels :

Ce schéma n'est réalisable qu'au moyen d'une boucle. Mais voilà, l'ordinateur ne saura pas quand arrêter de lire les paramètres optionnels... Il faut donc sortir de la boucle à un moment, et pensez à cela dès que vous écrirez le prototype de votre fonction ! Il existe plusieurs solutions, les voici.

Première solution : passer en paramètre obligatoire le nombre de paramètres optionnels



Le principe est ici de faire le tour de la boucle un certain nombre de fois. Ce nombre est le nombre de paramètres optionnels.

L'ordinateur doit absolument connaître ce nombre, ce sera donc un nombre passé en paramètre obligatoire :
Code : C
1
void multiplication(int *resultat, int nombreNombresAMultiplier, ...); //cette fonction pourrait multiplier ses paramètres optionnels


Deuxième solution : envoyer comme dernier paramètre optionnel une certaine valeur



Ici, le principe est de faire des tours de boucle, tant que l'ordinateur ne lit pas dans les paramètres optionnels, une certaine valeur donnée.

Dès que l'ordinateur obtiendra le paramètre optionnel ayant une certaine valeur, on sort de la boucle. Cette valeur est en réalité toujours NULL :
Code : C
1
void multiplication(int *resultat, ...); //cette fonction pourrait multiplier ses paramètres optionnels, mais il ne faut pas oublier d'envoyer NULL comme dernier paramètre optionnel


Bien sûr, vous n'aurez pas toujours à prendre la première ou la deuxième solution. Vous pourrez faire appel à d'autres alternatives, comme pour la fonction printf par exemple : à chaque %ld, %c, %s... la fonction va obtenir un paramètre optionnel. En effet, chaque %ld, %c, %s... correspond à un paramètre optionnel ;) .


Appel de la fonction



Je ne devrais même pas faire cette partie, c'est tellement simple d'appeler une fonction à nombre variable de paramètres :lol: !

Voici donc des exemples d'utilisation de fonctions :
Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Avec comme paramètre obligatoire le nombre de paramètres optionnels :
void multiplication(int *resultat, int nombreNombresAMultiplier, ...); //prototype
multiplication(&variable, 2, 5, 3); // 2 nombres à multiplier : 5 et 3
multiplication(pointeur, 4, 5, 3, 7, 9); // 4 nombres à multiplier : 5, 3, 7 et 9

// Avec NULL comme dernier paramètre optionnel
void multiplication(int *resultat, ...);
multiplication(&variable, 5, 3, NULL);
multiplication(pointeur, 5, 3, 7, 9, NULL);

// En "détournant" une des deux premières solutions pour lire tous les paramètres optionnels
void fprintf(FILE *fichier, char texte[], ...);
fprintf(fichier, "Entre %s et %s, il y a %ld km", "Lille", "Marseille", 1004);
fprintf(fichier, "Entre %s et %s, il y a %ld km, c'est %s !", "Lille", "Marseille", 1004, "beaucoup");

Contenu de la fonction

C'est fini pour la théorie, place à la pratique :pirate: ! Pour vous apprendre à créer une fonction à nombre variable de paramètres, je vous propose d'écrire ma fameuse fonction multiplication.

La réalisation de la fonction nécessite un nouveau type de variable et trois macros définis dans le fichier stdarg.h de la bibliothèque standard du C. Incluez donc le code #include <stdarg.h> dans le fichier qui contient la fonction ;) .


Vous pouvez déjà commencer par créer cette fonction.
Code : C
1
2
3
void multiplication(int *resultat, int nombreNombresAMultiplier, ...){
	// Contenu de la fonction
}


La variable de type va_list



Nous allons d'abord déclarer une variable qui permettra de repérer le paramètre lu actuellement par l'ordinateur. Cette variable est souvent appelée ap par les pros , ce qui signifie argument pointer ;) . Cette variable un peu spéciale sera de type va_list. Voici donc notre code :
Code : C
1
2
3
void multiplication(int *resultat, int nombreNombresAMultiplier, ...){
	va_list ap;
}


La macro va_start



Il va maintenant falloir indiquer à l'ordinateur le premier paramètre qu'il va devoir lire : ce sera le premier paramètre optionnel. Pour indiquer cela à l'ordinateur, il faudra utiliser une autre macro : va_start. Cette macro prend en paramètres la variable ap et le nom du dernier paramètre obligatoire. Ainsi notre code sera :

Code : C
1
2
3
4
5
void multiplication(int *resultat, int nombreNombresAMultiplier, ...){
	va_list ap;

	va_start(ap, nombreNombresAMultiplier);
}


La macro va_arg



Et maintenant, il faut obtenir la valeur de chaque paramètre optionnel. Chaque appel à la macro va_arg va nous retourner un paramètre optionnel. Le premier appel à cette macro va nous retourner le premier paramètre optionnel, le deuxième appel nous retournera le paramètre optionnel suivant, et ainsi de suite... Cette macro prend en compte 2 paramètres : ap et le type du paramètre à obtenir.

Le type du paramètre à obtenir n'est pas forcément le même pour chaque paramètre optionnel. Mais il faut d'une manière ou d'une autre obtenir son type. D'ailleurs, nous construirons tout à l'heure une fonction dont les paramètres optionnels ne sont pas tous du même type.


Notre petit algorithme consistera à multiplier un produit par le paramètre optionnel lu.

Avec le nombre de paramètres optionnels en paramètre obligatoire



Pour lire tous les paramètres nous allons utiliser une boucle de type for, qui sera lue tant qu'une variable initialisée à 0 et incrémentée à chaque tour de boucle sera strictement inférieure au nombre de paramètres optionnels. Voici donc notre code :
Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
void multiplication(int *resultat, int nombreNombresAMultiplier, ...){
	int i;
	*resultat = 1;
	va_list ap;

	va_start(ap, nombreNombresAMultiplier);

	for( i = 0 ; i < nombreNombresAMultiplier ; i++){
		*resultat *= va_arg(ap, int);
	}
}


Avec NULL comme dernier paramètre optionnel



On utilisera maintenant une boucle de type do{}while; ou while{} qui vérifiera à chaque tour si le paramètre lu n'est pas NULL. Voici donc la fonction :
Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
void multiplication(int *resultat, ...){
	int parametre = 1; // Valeur du paramètre actuel
	*resultat = 1;
	va_list ap;

	va_start(ap, resultat);
	
	do{ // On fait ce qui suit...
		*resultat *= parametre; // on multiplie le produit par le paramètre actuel
		parametre = va_arg(ap, int); // on obtient le paramètre actuel
		
	}while(parametre != NULL); // ...Tant qu'on ne trouve pas le paramètre NULL
	
	va_end(ap);
}


Ne partez pas déjà lancer vos compilateurs, MALHEUREUX :diable: ! Il reste encore une petite chose à faire !


La macro va_end



On doit (encore) appeler une macro, cette macro doit être utilisée après va_arg, il s'agit de va_end. Cette macro n'admet qu'un paramètre : ap. Et voilà notre fonction finale...

...si le nombre de paramètres optionnels est envoyé en paramètre obligatoire


Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
void multiplication(int *resultat, int nombreNombresAMultiplier, ...){
	int i;
	*resultat = 1;
	va_list ap;

	va_start(ap, nombreNombresAMultiplier);

	for( i = 0 ; i < nombreNombresAMultiplier ; i++){
		*resultat *= va_arg(ap, int);
	}

	va_end(ap);
}

...si le dernier paramètre optionnel est NULL


Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
void multiplication(int *resultat, ...){
	int parametre = 1; // Valeur du paramètre actuel
	*resultat = 1;
	va_list ap;

	va_start(ap, resultat);
	
	do{ // On fait ce qui suit...
		*resultat *= parametre; // on multiplie le produit par le paramètre actuel
		parametre = va_arg(ap, int); // on obtient le paramètre actuel
		
	}while(parametre != NULL); // ...Tant qu'on ne trouve pas le paramètre NULL
	
	va_end(ap);
}


Maintenant vous pouvez lancer vos compilateurs MALHEUREUX :diable: ! Le codage de la fonction à nombre variable de paramètres est désormais terminé ;) !

Exercices

Vous savez désormais utiliser des fonctions à nombre variable de paramètres. Mais je vous conseille fortement de faire les exercices suivants afin de vous entraîner et progresser :) .

Exercices non-corrigés



Addition de plusieurs termes



On commence par quelque chose de très facile. En effet on veut ajouter plusieurs termes, c'est presque la même chose qu'avec le produit.

Si vous n'y arrivez pas, je vous conseille fortement de relire ce tutoriel.
Et si vous avez besoin d'aide pour les exercices suivants, ou autre chose en rapport avec ce tutoriel, n'hésitez pas à m'envoyer un MP ou à consulter et poster un topic sur le forum !


Concaténer plusieurs chaînes de caractères



Il s'agit d'ajouter plusieurs chaînes les unes à la suite des autres. Libre à vous d'ajouter toutes les chaînes dans la première ou dans une nouvelle chaîne vierge ;) .

Je vous préviens, il y a une astuce qui permet de faire cet exercice très facilement...
Secret (cliquez pour afficher)
Croyiez-vous vraiment que j'allais donner cette astuce :diable: ? Bon... Un indice : rendez-vous au chapitre sur les chaînes de caractères du tutoriel en C de M@teo21


Jeu de questions/réponses



Le principe du jeu est de poser une question avec plus ou moins de réponses possibles. L'utilisateur choisit la réponse, puis l'ordinateur dit si la réponse est bonne ou non.

Grâce à une fonction de ce type... :
Code : C
1
void poserQuestionTresDifficileNiark(char *question, int score, int numeroBonneReponse, int nbreReponses, ...);

...ajouter, supprimer ou modifier une question sera alors un jeu d'enfant :lol: . Ce qui rendra le travail du programmeur (vous) bien plus simple (après avoir fait cette fonction), bien plus rapide ! En effet votre fonction, où se trouve le jeu, ne sera qu'une succession d'appels à poserQuestionTresDifficileNiark, alors que ça aurait été un codage bien plus complexe autrement !

Exercice corrigé



Principe de mon exercice



Le principe de mon exercice est assez simple : créer une fonction similaire à la fonction fprintf.

Si vous ne vous rappelez plus de cette fonction, allez vous renseigner ici !


J'ai appelé la fonction ecff (pour : Écrire une Chaîne Formatée dans un Fichier), elle doit avoir un prototype du type :
Code : C
1
void ecff(FILE *fichier, char texte[], ...);


J'ai remplacé les %ld, %lf, %s... par d'autres signes :
type de variablesigne à inclure dans la chaîne
int %a
long %b
float ou double %c
double %d
une chaîne %e
faire apparaître le '%' %%

Ainsi, cet appel à la fonction ecff... :
Code : C
1
ecff(fichier, "Pour aller de %e a %e en %e, il faut %ah%a.", "Rennes", "Paris", "TGV", 2, 15);


...donnera le fichier suivant :
Image utilisateur
À vous de jouer ;) !

Correction



Le principe de la fonction est très simple... On va lire chaque caractère du paramètre obligatoire texte et :


Attention cependant, il ne faut pas directement écrire les nombres dans le fichier : il faut passer par l'intermédiaire d'une chaîne de caractères !


Ainsi on obtient ce code :
Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
void ecff(FILE* fichier, char texte[], ...){
	int tailleChaine = 0, i;
	char chaineUtilisable[16] = {0}; //chaîne intermédiaire pour écrire dans le fichier
	va_list ap;
	va_start(ap, texte);
	
	// calcul de la taille de la chaîne à écrire dans un fichier
	tailleChaine = strlen(texte);
		
	// parcours de chaque caractère
	for(i = 0; i < tailleChaine; i++){
		if(texte[i] == '%'){
			i++; // on se place au caractère suivant pour savoir ce que c'est
			switch(texte[i]){
				case 'a' :
					sprintf(chaineUtilisable, "%ld", va_arg(ap, int));
					fputs(chaineUtilisable, fichier);
					break;
				case 'b' :
					sprintf(chaineUtilisable, "%ld", va_arg(ap, long));
					fputs(chaineUtilisable, fichier);
					break;
				case 'c' :
					sprintf(chaineUtilisable, "%lf", va_arg(ap, float));
					fputs(chaineUtilisable, fichier);
					break;
				case 'd' :
					sprintf(chaineUtilisable, "%lf", va_arg(ap, double));
					fputs(chaineUtilisable, fichier);
					break;
				case 'e' :
					fputs(va_arg(ap, char*), fichier);
					break;
				case '%' :
					fputc('%', fichier);
					break;
			}
		}
		else{
			fputc(texte[i], fichier);
		}
	}
	va_end(ap);
}


Bien sûr vous n'avez peut-être pas fait pareil, et je ne prétends pas avoir fait le meilleur code possible :honte: ! En réalité, la fonction printf est plus complexe : les spécificateurs de formats (%s, %ld ...) sont composés d'un % suivi d'une combinaison de caractères (par exemple %s, %ld, %#X ou %8.2f). Je ne vais pas m'étendre là-dessus car ça n'est pas le sujet du tutoriel, bien que cela soit très intéressant ^^ . Sachez en tout cas que l'on peut trouver pleins de trucs sur cette fonction en recherchant "printf" sur notre ami Google :-° .

Q.C.M.

Voici 4 appels ou prototypes de fonctions :
Code : C
1
2
3
4
/*1*/ fonction(parametreA, parametreB, parametreC, ...);
/*2*/ fonction(parametreA, parametreB, parametreC)
/*3*/ void fonction(int parametreA, int parametreB, double parametreC);
/*4*/ void fonction(int parametreA, int parametreB, int parametreC, ...)


À votre avis, parmi ces fonctions, une seule fonction est valide (laquelle) ? Plusieurs le sont ? Ou aucune ne l'est ?
va_arg est...
Avez-vous réussi à faire tous les exercices ? Répondez sincèrement.

Statistiques de réponses au QCM


N'hésitez pas à continuer à vous entraîner ! Et même si vous ne voyez pas l'intérêt des fonctions à nombre variable de paramètres dans l'immédiat, ne les oubliez pas ! Vous trouverez cela bien pratique quand vous en aurez besoin ! Pour ma part, j'en ai déjà utilisé ;) .

N'hésitez pas non plus à poster des commentaires, et si vous le faites, n'hésitez pas à me dire quel(s) point(s) améliorer dans mon tutoriel ;) .

Voilà, ce tutoriel est maintenant fini. Vous savez tout sur les fonctions à nombre variable de paramètres :D ! J'espère que mon tutoriel vous à plu :) .
Une prochaine version du tutoriel est en préparation. Cette future version plus ou moins majeure approfondira les notions abordées dans le tutoriel (le type va_list, les macros...), et l'enrichira (j'y ajouterai également deux nouveaux exercices). A bientôt donc (fin décembre début janvier si tout va bien) !
Retour en haut Retour en haut


Créé : le 17/05/2008 à 21:21:14
Modifié : le 12/11/2008 à 10:40:50
Avancement : 100%
Licence : Copie non autorisée

14 commentaires

Changer de design | En savoir plus | Plan du site | Politique d'accessibilité | Règles | RSS tutoriels | RSS news
Édité par Simple IT SARL : Nous contacter | Notre blog | Revue de presse | Publicité

Y'a plus rien à lire, faut remonter maintenant !

Hébergement web - Correction de tutoriels - Créer un site
Vous souhaitez apparaître ici ? Contactez-nous.

Nombre de connectés 137 Zéros connectés | Requêtes SQL 8 requêtes | Temps de génération de la page : Total (SQL) 0.0768s (0.0421s)