1.6. Les fonctions
Le C++ ne permet de faire que des fonctions, pas de procédures. Une procédure peut être faite en utilisant une fonction ne renvoyant pas de valeur ou en ignorant la valeur retournée.
1.6.1. Définition des fonctions
La définition des fonctions se fait comme suit :
type identificateur(paramètres)
{
... /* Instructions de la fonction. */
}
type est le type de la valeur renvoyée, identificateur est le nom de la fonction, et paramètres est une liste de paramètres. La syntaxe de la liste de paramètres est la suivante :
type variable [= valeur] [, type variable [= valeur] [...]]
où type est le type du paramètre variable qui le suit et valeur sa valeur par défaut. La valeur par défaut d'un paramètre est la valeur que ce paramètre prend si aucune valeur ne lui est attribuée lors de l'appel de la fonction.
Note : L'initialisation des paramètres de fonctions n'est possible qu'en C++, le C n'accepte pas cette syntaxe.
La valeur de la fonction à renvoyer est spécifiée en utilisant la commande return, dont la syntaxe est :
return valeur;
Exemple 1-13. Définition de fonction
int somme(int i, int j)
{
return i+j;
}
Si une fonction ne renvoie pas de valeur, on lui donnera le type void. Si elle n'attend pas de paramètres, sa liste de paramètres sera void ou n'existera pas. Il n'est pas nécessaire de mettre une instruction return à la fin d'une fonction qui ne renvoie pas de valeur.
Exemple 1-14. Définition de procédure
void rien() /* Fonction n'attendant pas de paramètres */
{ /* et ne renvoyant pas de valeur. */
return; /* Cette ligne est facultative. */
}
1.6.2. Appel des fonctions
L'appel d'une fonction se fait en donnant son nom, puis les valeurs de ses paramètres entre parenthèses. Attention ! S'il n'y a pas de paramètres, il faut quand même mettre les parenthèses, sinon la fonction n'est pas appelée.
Exemple 1-15. Appel de fonction
int i=somme(2,3);
rien();
Si la déclaration comprend des valeurs par défaut pour des paramètres (C++ seulement), ces valeurs sont utilisées lorsque ces paramètres ne sont pas fournis lors de l'appel. Si un paramètre est manquant, alors tous les paramètres qui le suivent doivent eux aussi être omis. Il en résulte que seuls les derniers paramètres d'une fonction peuvent avoir des valeurs par défaut. Par exemple :
int test(int i = 0, int j = 2)
{
return i/j;
}
L'appel de la fonction test(
est valide. Comme on ne précise pas le dernier paramètre, j est initialisé à 2. Le résultat obtenu est donc 4. De même, l'appel test() est valide : dans ce cas i vaut 0 et j vaut 2. En revanche, il est impossible d'appeler la fonction test en ne précisant que la valeur de j. Enfin, l'expression « int test(int i=0, int j) {...} » serait invalide, car si on ne passait pas deux paramètres, j ne serait pas initialisé.
1.6.3. Déclaration des fonctions
Toute fonction doit être déclarée avant d'être appelée pour la première fois. La définition d'une fonction peut faire office de déclaration.
Il peut se trouver des situations où une fonction doit être appelée dans une autre fonction définie avant elle. Comme cette fonction n'est pas définie au moment de l'appel, elle doit être déclarée. De même, il est courant d'avoir à appeler une fonction définie dans un autre fichier que le fichier d'où se fait l'appel. Encore une fois, il est nécessaire de déclarer ces fonctions.
Le rôle des déclarations est donc de signaler l'existence des fonctions aux compilateurs afin de les utiliser, tout en reportant leur définition plus loin ou dans un autre fichier.
La syntaxe de la déclaration d'une fonction est la suivante :
type identificateur(paramètres);
où type est le type de la valeur renvoyée par la fonction, identificateur est son nom et paramètres la liste des types des paramètres que la fonction admet, éventuellement avec leurs valeurs par défaut, et séparés par des virgules.
Exemple 1-16. Déclaration de fonction
int Min(int, int); /* Déclaration de la fonction minimum */
/* définie plus loin. */
/* Fonction principale. */
int main(void)
{
int i = Min(2,3); /* Appel à la fonction Min, déjà
déclarée. */
return 0;
}
/* Définition de la fonction min. */
int Min(int i, int j)
{
if (i<j) return i;
else return j;
}
Si l'on donne des valeurs par défaut différentes aux paramètres d'une fonction dans plusieurs déclarations différentes, les valeurs par défaut utilisées sont celles de la déclaration visible lors de l'appel de la fonction. Si plusieurs déclarations sont visibles et entrent en conflit au niveau des valeurs par défaut des paramètres de la fonction, le compilateur ne saura pas quelle déclaration utiliser et signalera une erreur à la compilation. Enfin, il est possible de compléter la liste des valeurs par défaut de la déclaration d'une fonction dans sa définition. Dans ce cas, les valeurs par défaut spécifiées dans la définition ne doivent pas entrer en conflit avec celles spécifiées dans la déclaration visible au moment de la définition, faute de quoi le compilateur signalera une erreur.
1.6.4. Surcharge des fonctions
Il est interdit en C de définir plusieurs fonctions qui portent le même nom. En C++, cette interdiction est levée, moyennant quelques précautions. Le compilateur peut différencier deux fonctions en regardant le type des paramètres qu'elle reçoit. La liste de ces types s'appelle la signature de la fonction. En revanche, le type du résultat de la fonction ne permet pas de l'identifier, car le résultat peut ne pas être utilisé ou peut être converti en une valeur d'un autre type avant d'être utilisé après l'appel de cette fonction.
Il est donc possible de faire des fonctions de même nom (on les appelle alors des surcharges) si et seulement si toutes les fonctions portant ce nom peuvent être distinguées par leurs signatures. La surcharge qui sera appelée sera celle dont la signature est la plus proche des valeurs passées en paramètre lors de l'appel.
Exemple 1-17. Surcharge de fonctions
float test(int i, int j)
{
return (float) i+j;
}
float test(float i, float j)
{
return i*j;
}
Ces deux fonctions portent le même nom, et le compilateur les acceptera toutes les deux. Lors de l'appel de test(2,3), ce sera la première qui sera appelée, car 2 et 3 sont des entiers. Lors de l'appel de test(2.5,3.2), ce sera la deuxième, parce que 2.5 et 3.2 sont réels. Attention ! Dans un appel tel que test(2.5,3), le flottant 2.5 sera converti en entier et la première fonction sera appelée. Il convient donc de faire très attention aux mécanismes de surcharge du langage, et de vérifier les règles de priorité utilisées par le compilateur.
On veillera à ne pas utiliser des fonctions surchargées dont les paramètres ont des valeurs par défaut, car le compilateur ne pourrait pas faire la distinction entre ces fonctions. D'une manière générale, le compilateur dispose d'un ensemble de règles (dont la présentation dépasse le cadre de ce livre) qui lui permettent de déterminer la meilleure fonction à appeler étant donné un jeu de paramètres. Si, lors de la recherche de la fonction à utiliser, le compilateur trouve des ambiguïtés, il génére une erreur.