Les fonctions :
Lorsqu’il y a de nombreuses lignes de code dans un programme, celui-ci peut vite devenir illisible, rendant ainsi d’éventuelles corrections d’erreurs et la maintenance fastidieuses.
D’où l’intérêt de décomposer le programme en sous-programmes capable d’apporter chacun une solution à un sous-problème particulier, d'un problème global à résoudre.
Une fonction est un sous-programme conçu pour exécuter une tâche spécifique et bien déterminée. C’est l'outil de création des sous-programmes en langage C.
Tout programme en C possède au moins une fonction (la fonction main() par exemple). Grâce aux fonctions un programme peut être découpé en plusieurs modules facilement gérables (programmation modulaire).
Considérons par exemple la fonction mathématique f(x) = 2x2 + 3x + 6
Par analogie au langage C:
⇒ Le terme f(x) :
- f représente le nom de la fonction,
- x c’est le paramètre.
⇒ L’expression 2x2 + 3x + 6: c'est la définition de la fonction.
Supposons que l’on veut calculer f(4). Cela veut dire qu’on passe la valeur 4 à la fonction f : donc partout dans l’expression (définition) de la fonction, x sera remplacé par 4 ; ainsi
f(4) = 2x42 + 3x4 + 6 = 50.
En langage C, la fonction reçoit les valeurs des paramètres depuis le programme qui l'appelle, puis elle effectue la tâche pour laquelle elle a été crée, et renvoie un résultat.
Syntaxe :
type identificateur(type1 arg1, .., type_n arg_n)
{
déclarations ;
instructions ;
return expression ;
}
La ligne : type identificateur(type1 arg1, .., type_n arg_n) correspond à l’en-tête de la fonction.
Le type correspond au type de la valeur renvoyée par la fonction en retour suite à un appel par un programme.
L’identificateur c’est le nom de la fonction.
type1 arg1, ..., type_n arg_n constitue la liste de paramètres (ou arguments) optionnels qui peuvent être envoyés à la fonction.
Le bloc d’instructions à l’intérieur des accolades représente le corps de la fonction. Dans ce bloc, l’instruction return renvoie au programme appelant une valeur qui peut prendre la forme d’une expression.
Exemple de fonction :
int fexple(int x, int y)
{
int z ;
z = (x <= y) ? x : y ;
return z ;
}
Amélioration de l'exemple précédent:
int fexple(int x, inty)
{
return((x >=y )? x : y) ;
}
Lorsque le type de l’expression figurant dans return est différent du type du résultat tel qu’il a été déclaré dans l’en-tête, le compilateur mettra en place des moyens de conversion.
Pour ce qui est du compilateur MPLAB XC8, le type de l’expression figurant dans return devra être identique au type définit dans l’en-tête de la fonction.
Une fonction peut avoir plusieurs instructions return. Mais une seule sera exécutée, et les expressions figurant dans les return seront toutes du même type que celui de l'en-tête.
Exemple :
int bigger(int a, int b)
{
if (a >b)
return 1 ;
else
return 0 ;
}
Un type de fonction est « void » si l’instruction return n’a aucune expression ou si elle n’existe simplement pas. Cette famille de fonctions est souvent appelé procédure car elle ne revoie rien lorsqu’elle est appelée.
Exemple :
void identificateur(type1 arg1, .., type_n arg_n)
{
déclarations ;
instructions ;
return ;
}
Dans ce cas la présence de l’instruction return est facultative.
Paramètres d'une fonction:
Ils sont déclarés comme des variables ordinaires entre parenthèses séparés par des virgules. Les paramètres d'une fonction ne sont valides qu’à l’intérieur de celle-ci.
Les paramètres d’une fonction peuvent être de types différents :
int baar(int x, float y, char z).
Les paramètres du même type doivent être déclaré séparément :
Ainsi, la déclaration suivante: int bigger(int a, b) n’est pas valide.
En revanche int bigger(int a, int b) est une écriture correcte.
Si aucun paramètre n’est requis, il faudra utiliser le mot void en lieu et place des paramètres dans l’en-tête de la fonction:
Exemple :
type identificateur (void)
{
déclarations ;
instructions ;
return ;
}
Appel d’une fonction :
Pour appeler une fonction, il faut utiliser l’une des syntaxes suivantes :
baar() ; // lorsque la fonction ne renvoie aucune valeur, ni ne reçoit aucun paramètre.
a = baar() ; // lorsque la fonction ne reçoit aucun paramètre, mais renvoie une valeur.
baar(x, y) ; // lorsque la fonction reçoit des paramètres mais ne renvoie aucune valeur.
a = baar(x, y) ; // lorsque la fonction reçoit des paramètres et renvoie une valeur.
Prototype d’une fonction :
Comme une variable, une fonction devra être déclarée. Cette déclaration sera faite avant la fonction main(), ou d’autres fonctions susceptibles de l’appeler.
Il y a deux façons de déclarer une fonction :
- Soit en la définissant complètement,
- Ou par son prototype. Dans ce cas, la définition complète de la fonction peut être située à n’importe quel endroit du programme.
Le prototype d’une fonction a deux formats:
- Sous la forme d'une copie exacte de l’en-tête de la fonction : par exemple :
int bigger(int x, int y) ;
- Ou sous une forme semblable à l’en-tête de la fonction sans noms des paramètres, mais leur type devra être présent : exemple :
int bigger(int, int) ;
Exemples d’utilisation et de déclaration :
Exemple 1 :
int a=5, b=10, c ;
int maximum(int x, int y)
{
return((x>=y) ? x : y) ;
}
int main(void)
{
c = maximum(a, b) ;
printf(« La valeur maximale est %d », c) ;
}
Exemple 2 :
Résulte de transformation d’écriture de l’exemple 1, avec un résultat identique :
int a=5, b=10, c
int maximum(int x, int y) ;
int main(void)
{
c = maximum(a, b) ;
printf(« La valeur maximale est %d », c) ;
}
int maximum(int x, int y)
{
return((x>=y) ? x : y) ;
}
Passage de paramètres par valeur :
Lorsqu’on passe un paramètre à une fonction, on effectue un passage par valeur. La valeur passée à la fonction est copiée dans la variable du paramètre local, la fonction travaille sur une copie de cette variables et ne pourra pas la modifier.
Exemple :
int a, b, c ;
int baar(int x, int y) ;
int main(void)
{
a = 5 ;
b = 10 ;
c = baar(a, b) ;
}
int baar(int x, int y)
{
x = x + (++y) ;
return x ;
}
La valeur de a est copiée dans la variable x (lignes 8 et 11) ; la valeur de b est copiée dans la variable y (lignes 8 et 11). La fonction change pas ces deux valeurs, et effectue le traitement qui lui est demandé.
Fonctions récursives :
Une fonction peut s’appeler elle-même de façon répétitive. On parle de fonctions récursives. Ce type de fonctions est utile pour les calculs où chaque action dépend du résultat précédent.
Exemple, calcul de la factorielle d’un nombre.
long int factoriel (int n)
{
if n <= 1
return(1) ;
else
return(n * factoriel(n-1)) ;
}
Evaluation de la fonction pour n = 5 :
Lors de l’appel, factoriel(5);
5 étant supérieur à 1, le programme évalue d’abord 5*factoriel(4) ; ensuite factoriel(4) évalue 4*factoriel(3) ; puis factoriel(3) évalue 2*factoriel(2) ; ensuite factoriel(2) évalue 2*factoriel(1) ; factoreil(1) = 1 (instruction ligne 3) et au final
C = 5 × 4 × 3 × 2 × 1 = 120.
Les variables globales :
En langage C, les fonctions échanges les informations grâce à la transmission des paramètres et à la récupération d’une valeur de retour. Les fonctions, y compris la fonction main() peuvent aussi partager des variables communes. Ce type de variable se nomme variable globale.
Exemple :
int x = 5 ;
int fexple1(int y)
{
return(x + y) ;
}
int fexple2(int z)
{
return (x + z) ;
}
int main (void)
{
int a = 8;
int b ;
b = fexple1(a) ;
…..
}
Ligne 1 : x est valide pour toutes les fonctions qui sont situées après sa déclaration.
Lignes 3 et 5 : y n’est connu que de la fonction fexple1() ; z n’est connu que de la fonction fexple2() ; a et b ne sont connus que par la fonction main().
Les variables globales sont valides (ou encore ont une portée) sur toute la partie du code source qui suit leur déclaration.
Variable locale :
Lorsqu’on déclare les variables lors de la définition et dans un bloc d’une fonction, celles-ci ne sont connues que par cette fonction elle-même. De telles variables sont dites locales à la fonction où elles sont déclarées.
Portée d’une variable dans une fonction :
Les variables locales ne sont connues qu’à l’intérieur de la fonction dans laquelle elles sont déclarées. Leur portée est limitée à cette fonction. Elles ne sont pas accessibles à l’extérieur de la fonction.
Exemple:
int x ;
int baar(int n)
{
int a ;
a /= n ;
return(a) ;
}
int main(void)
{
a = baar(4) ;
a = x ;
}
La ligne 12 va générer une erreur lors de la compilation, car la variable a ne peut pas être accessible à l’extérieure de la fonction dans laquelle elle a été déclarée.
Les variables locales n’ont aucun lien ni avec les variables locales de même nom déclarées dans d’autres fonctions, ni avec les variables globales de même nom.
Dans cette illustration :
int n ;
long int factoriel(int n)
{
….
}
La variable n de la première ligne, n’est pas la même que la variable n de la seconde ligne.
Règle de priorité :
Exemple 1:
int n ;
int baar(int n)
{
…
x *= n ;
…
}
La variable n déclarée dans la fonction est prioritaire sur la variable globale n déclarée en début de programme.
Dans cet autre exemple, n est reconnue par la fonction baar():
int n ;
int baar(int y)
{
…
x += (n + y);
…
}
Pour finir, les fonctions permettent de pratiquer la programmation modulaire. Elles facilitent la maintenance du code, et la création de programmes réutilisables.