CM #3 : Fonctions en C
Objectifs
Dans ce cours, nous allons définir
  • Quand utiliser des fonctions ?
  • Syntaxe, déclaration, définition, appel des fonctions
  • Notion de valeur de retour et paramètres formels et effectifs

La conception structurée descendante est une méthodologie pour créer des programmes complexes. Le principe, relativement standard, est de décomposer un problème complexe en un ensemble de problèmes plus simples et de résoudre chacun de ces problèmes simples (en appliquant éventuellement le même principe). Cette méthodologie est appliquée dans un grand nombre de domaines et pour une grande variété de problèmes: gestion de projets, conduite de travaux, jeux, stratégies…

Les objectifs et intérêts d’une telle méthodologie sont multiples:

  • simplification: permet d’appréhender des problèmes complexes en les découpant en une succession de tâches plus simples qui sont elles-mêmes découpables en tâches ou instructions élémentaires.
  • abstraction: permet de se concentrer sur le cœur du problème en évitant les détails techniques ou spécificités du langage.
  • structuration: faciliter la maintenance du programme en permettant de facilement retrouver certaines fonctionnalités ou parties du code.
  • réutilisation: certaines tâches peuvent être adaptées d’autres problèmes et donc cette méthodologie permet de réutiliser ce qui a déjà été écrit (cf printf, scanf…).

Pour cela, on introduit les fonctions paramétrées qui permettent de réaliser des tâches qui sont répétées fréquemment. Ces fonctions sont des sous-programmes qui peuvent être appelés par le programme principal ou par un autre sous-programme. Ces tâches peuvent modifier leur comportement suivant certaines valeurs: les paramètres (cf printf, scanf…).

Fonctions

Définition

Définition d'une fonction
Une fonction est un sous-programme qui à partir d'un ensemble (éventuellement vide) de données produit un et un seul résultat.

Voici, à l’aide d’un exemple, la syntaxe de définition d’une fonction. La fonction détermine le maximum de deux entiers.

int max(int a, int b)
{
    int m;

    if (a < b)  m = b;
    else        m = a;

    return m;
}

Un certain nombre d’éléments sont remarquables sur cet exemple:

  • la première ligne indique quel est le type de retour de la fonction (ici int) puisqu’une fonction fournit obligatoirement un résultat, quel est son nom et quels sont les paramètres de cette fonction ainsi que leur type. Tous les paramètres présents sur la première ligne (entre parenthèses) doivent obligatoirement être définis.
  • ensuite on définit des variables locales, c’est à dire des variables temporaires, utiles uniquement pour la fonction.
  • ensuite se trouve le corps de la fonction: un ensemble d’instructions
  • enfin le mot-clé return suivi d’une variable ou d’une expression indique quelle valeur retourne la fonction. Bien entendu la variable / expression qui suit return doit être du même type que celui spécifié sur la première ligne.
Remarque
Il est possible d'avoir plusieurs fois le mot-clé return dans une fonction, seul le premier rencontré importe, les autres ne sont pas pris en compte.

Appel

Une fois définie cette fonction peut-être utilisée par un programme ou un sous-programme. Comme par exemple dans l’exemple suivant où la fonction max est appelée avec différents jeux de paramètres:

int main()
{
    int x = 5, y = 19, z;

    z = max(4, 7);
    z = max(4, x);
    z = max(x, y);
    z = max(2 * x, y);

    return 0
}
Vocabulaire
Pour éviter les confusions, on définit pour les fonctions et actions deux types de paramètres:
  • Les paramètres formels: il s'agit des variables utilisées dans le corps du sous-programme (par exemple a et b dans la fonction max).
  • Les paramètres effectifs: il s'agit de la valeur / variable / expression évaluée qui est fournie lors de l'appel du sous-programme (par exemple 4 et 7 pour le premier appel de max)

L’appel de la fonction max dans le programme ci-dessus implique un certain nombre d’étapes:

  • les paramètres effectifs de la fonction sont remplacés par leur valeur s’il s’agit de variables ou d’expression. Ainsi max est successivement appelé avec (4, 7), (4, 5), (5, 19), (10, 19)
  • ces valeurs sont affectés au paramètres formels de la fonction, la première valeur étant pour la variable a, la seconde pour b. Ainsi pour le premier appel de max, a vaut 4 et b vaut 7.
  • le corps de la fonction est exécuté jusqu’à rencontrer le mot-clé return
  • le résultat retourné par la fonction est la valeur de l’expression qui suit return. Pour le premier appel de max, la valeur retournée est 4.
  • cette valeur retournée est affectée à une variable (ici z).

Procédures

Dans certains cas, le sous-programme ne nécessite pas de retourner de valeur (par exemple pour imprimer des choses à l’écran). Dans ce cas on parle de procédures et en C, la syntaxe est la même que pour une fonction sauf que:

  • le type de retour est void
  • les return ne sont suivis d’aucune valeur / variable / expression

Par exemple:

#include <stdio.h>

void print_max(int a, int b)
{
    int maxi;

    if  (a < b)     maxi = b;
    else        maxi = a;
    printf("Le maximum de %d et %d est : %d\n", a, b, maxi);
}

Limitations

Le principe de fonctions en C ne permet de retourner qu’au plus une valeur. Ceci est une limitation quand on souhaiterait retourner plusieurs valeurs avec une seule fonction. On verra dans un chapitre suivant (i.e. sur les pointeurs) comment contourner cette limitation. La solution proposée permettra en outre de régler un autre problème: certains types complexes ne peuvent pas / ne doivent pas être retournés.

Résumé

Voici ce qu’il faut retenir sur les fonctions et procédures

Fonction
Une fonction retourne un et un seul résultat.
// déclaration (optionnelle)
type_de_retour nom_de_fonction(liste_params);

// définition
void nom_de_fonction(liste_params)
{
    //liste_instructions
}

// appel
variable_de_type_compatible = nom_fonction(liste_expressions);
Procédure
Une procédure ne retourne pas de résultat.

Syntaxe:

// déclaration (optionnelle)
void nom_de_action(liste_params);

// définition
void nom_de_action(liste_params)
{
    liste_instructions
}

// appel
nom_action(liste_expressions)