Les fonctions sont les éléments de base d'un programme C. Un programme C bien conçu contient en général de nombreuses petites fonctions plutôt que peu de grosses fonctions. Chaque fonction contient une série d'instructions qui seront exécutées à chaque fois que cette fonction sera appelée.
Pour définir une fonction, il faut donner le type de la valeur qu'elle produit, son nom, la liste de ses paramètres et enfin le corps de la fonction :
type nom (type1 param1, ..., typeN paramN)
{
instruction 1
...
instruction N
}
Voici, par exemple, la définition d'une fonction qui calcule la moyenne de deux nombres :
double moyenne ( double a, double b )
{
return (a + b) / 2;
}
Le mot-clé return
est suivi d'une expression. La valeur
de cette expression sera le résultat de la fonction. La
dernière instruction d'une fonction doit être une instruction
return
permettant de renvoyer le résultat.
Il est possible d'utiliser return
n'importe où et
plusieurs fois dans une fonction. La fonction s'arrêtera
immédiatement et le résultat sera renvoyé. Néanmoins, dans le
cadre d'une programmation structurée, l'instruction
return
ne devrait apparaître que comme dernière
instruction sauf si cela nuit à la compréhension de la
fonction.
Pour appeler une fonction, il suffit de mettre son nom suivi
de la liste des valeurs à donner aux paramètres entre
parenthèses. Un appel de fonction est considérée comme une
expression du type du résultat de la fonction et la valeur de
cette expression est le résultat de l'exécution de la
fonction. Dans l'exemple suivant, c
contiendra la
valeur moyenne de 30 et 75 :
c = moyenne(30, 75);
Le type de la valeur retournée par une fonction peut être n'importe quel type (un type de base ou même une structure). La fonction suivante calcule la somme de deux nombres complexes :
complexe AddComplexe(complexe op1, complexe op2) {
complexe somme;
somme.reel = op1.reel + op2.reel;
somme.imag = op1.imag + op2.imag;
return somme;
}
Le type void
permet de spécifier que la fonction ne
renvoie aucun résultat. Dans ce cas, pour terminer l'exécution
de la fonction, on peut utiliser le mot-clé return
seul
ou plus simplement atteindre la fin des instructions.
Le type void
mit seul à la place de la liste des
arguments permet aussi de spécifier que la fonction ne prend
aucun argument. Voici une fonction qui ne fait qu'afficher un
message sur la sortie standard :
void AffMessage(void) {
printf("Voici un message\
n");
}
On appelle corps de la fonction le bloc d'instructions qui commence après l'accolade ouvrante qui suit la liste des paramètres et qui se termine par l'accolade fermante correspondante.
Comme tout autre bloc, le corps de la fonction peut débuter par une série de définitions de variables. Ces variables sont appelées variables locales. Chaque appel de la fonction crée de nouvelles instances des variables locales.
Les fonctions C sont réentrantes. Cela signifie qu'elles
peuvent être appelées récursivement. La suite de Fibonacci se
définit récursivement de la manière suivante :
La fonction C suivante permet de calculer Fib(n)(de manière très inefficace) :
unsigned long Fib(unsigned long n) {
unsigned long resultat;
if (n < 2L) {
resultat = 1L;
} else {
resultat = Fib(n - 1) + Fib(n - 2);
}
return resultat;
}
Il n'y a théoriquement pas de limite au nombre de paramètres (ou arguments) que peut prendre une fonction. Chaque paramètre de la liste des paramètres doit être défini par son type suivi de son nom. Comme pour le type du résultat, le type d'un paramètre peut être soit un type de base soit une structure ou une union.
Les paramètres peuvent être considérés comme des variables locales à la fonction. Lors de l'appel de la fonction, la valeur de chaque paramètre est recopiée dans un nouvel espace mémoire réservé pour ce paramètre.
Une fonction peut donc localement modifier le contenu de ses
paramètres. Les valeurs utilisées lors de l'appel ne seront
absolument pas modifiées. Dans l'exemple suivant, la variable
a
à la fin de la fonction f
reste égale à 1
puisque c'est la valeur de a
et non pas la variable
a
qui est envoyée à la fonction g
:
void g (int param) {
...
param = 150;
...
}
void f (void) {
int a = 1;
...
g(a);
...
}
Le passage des paramètres par valeur ne permet donc pas de modifier une variable de la fonction appelante. Il semble donc que la seule interaction que puisse avoir une fonction avec l'extérieur est le renvoi de son résultat (ou l'utilisation de variables globales). Mais en fait, grâce aux pointeurs, il est possible d'agir à distance. Il suffit pour cela d'envoyer non pas la valeur d'une variable mais son adresse en mémoire (c'est à dire un pointeur sur cette variable). La fonction appelée peut alors modifier le contenu de cet emplacement mémoire (et donc le contenu de la variable elle-même).
Voici une fonction qui divise une variable de type int
par un diviseur. Le résultat de la fonction est le reste de
cette division. Le premier paramètre est un pointeur sur la
variable à diviser et le second paramètre est le diviseur :
int DiviseEtReste(int * val, int diviseur) {
int reste = (*val) % diviseur;
*val /= diviseur;
return reste;
}
On utilise aussi souvent des pointeurs pour éviter de recopier des grosses structures ou pour travailler sur des tableaux de valeurs. Voici, par exemple, une fonction permettant de calculer la moyenne des valeurs stockées dans un tableau. Le premier paramètre est un pointeur sur le tableau et le second est le nombre de valeurs stockées :
double MoyenneTableau(double *Tab, int nbVal) {
double somme = 0;
int indice;
for (indice = 0; indice < nbVal; indice++) {
somme += Tab[indice];
}
return somme / nbVal;
}
Pour utiliser cette fonction, on peut l'appeler de la manière suivante :
void f(void) {
double Tableau[1000];
double moyenne;
...
/* initialisation des valeurs de Tableau */
...
moyenne = MoyenneTableau(Tableau, 1000);
...
}
La fonction dont le nom est main
est une fonction
particulière dans un programme C. C'est elle qui est
exécutée en premier lorsqu'on lance un programme. Le
programme s'arrête lorsque l'exécution de cette fonction se
termine.
Elle peut être définie de différentes manières. Un programme
simple définit main
de la manière suivante :
int main(void) {
...
}
Le type de main
est int
afin de permettre
de retourner au système d'exploitation un code d'erreur (0 si
tout se passe bien).
Elle peut aussi recevoir une liste d'arguments provenant du système d'exploitation (pour ceux qui le permettent comme UNIX par exemple). Elle est alors déclarée de la manière suivante :
int main( int argc, char * argv[]) {
...
}
argv
est un tableau de pointeurs sur des chaînes de caractères.
Chaque chaîne de ce tableau contient le texte d'un paramètre fourni
par le système d'exploitation. argc
contient le nombre de
paramètres stockés dans le tableau. argv[0]
contient en général
le nom du programme en cours. C'est par ce moyen qu'un programme C
peut récupérer les options d'une ligne de commande UNIX par exemple.
Il est possible d'utiliser des pointeurs sur des fonctions ou
de créer des fonctions à nombre de paramètres variables (comme
printf
) mais nous ne l'expliquerons pas dans ce
document car cela dépasse très largement le cadre d'une
introduction au C.