Une fonction est un bloc autonome d'instructions conçu pour accomplir une tâche précise. En langage C, la conception et l'appel de fonctions obéissent à des règles rigoureuses régies par le compilateur, notamment la déclaration obligatoire des prototypes et le mécanisme de passage par valeur.
1. Anatomie d'une fonction en C#
Une définition de fonction se compose d'un en-tête (signature) et d'un corps d'instructions :
float CalculerTVA(float montantHT, float tauxPourcent)
{
float tva = montantHT * (tauxPourcent / 100.0f);
return tva; // Renvoie le montant calculé à l'appelant
}
- Si la fonction ne renvoie aucune valeur, son type de retour est
void. - Si la fonction ne prend aucun paramètre d'entrée, on spécifie
(void).
2. Le rôle crucial des Prototypes de fonctions#
Le compilateur C lit votre code séquentiellement, de haut en bas. Si vous écrivez la fonction main() au début de votre fichier et que vous y appelez une fonction définie plus bas, le compilateur ne connaît ni son type de retour ni ses paramètres !
Pour remédier à cela, on sépare la déclaration (le prototype) de la définition (le corps de la fonction) :
flowchart TD
Proto["1. Prototypes placés en haut de fichier\nInforme le compilateur de l'existence des fonctions"]
Main["2. Fonction main()\nAppelle les fonctions en toute sécurité"]
Def["3. Définitions des fonctions en bas\nContient le code réel des fonctions"]
Proto --> Main --> Def
#include <stdio.h>
/* ======================================================== */
/* 1. PROTOTYPES DES FONCTIONS (Déclarations préalables) */
/* ======================================================== */
float CalculerPerimetreRectangle(float longueur, float largeur);
void AfficherResultat(float perimetre);
/* ======================================================== */
/* 2. PROGRAMME PRINCIPAL */
/* ======================================================== */
int main(void)
{
float L = 12.5f;
float l = 4.0f;
float p = CalculerPerimetreRectangle(L, l);
AfficherResultat(p);
return 0;
}
/* ======================================================== */
/* 3. DÉFINITIONS RÉELLES DES FONCTIONS */
/* ======================================================== */
float CalculerPerimetreRectangle(float longueur, float largeur)
{
return 2.0f * (longueur + largeur);
}
void AfficherResultat(float perimetre)
{
printf("Le perimetre du rectangle est de : %.2f cm\n", perimetre);
}
3. Le passage de paramètres par valeur (Pass by Value)#
[!IMPORTANT] En C, tous les paramètres scalaires (
int,float,char, etc.) sont transmis par valeur. Cela signifie que la fonction reçoit une copie indépendante de la donnée. Toute modification appliquée au paramètre à l'intérieur de la fonction n'a aucun impact sur la variable originale de l'appelant.
L'expérience classique de l'échange impossible#
Observons cette tentative d'écrire une fonction qui échange deux nombres :
#include <stdio.h>
void EchangerFaux(int a, int b)
{
int temporaire = a;
a = b;
b = temporaire;
// Les copies locales 'a' et 'b' ont été échangées...
}
int main(void)
{
int x = 10, y = 20;
EchangerFaux(x, y);
// x vaut TOUJOURS 10 et y vaut TOUJOURS 20 !
printf("x = %d, y = %d\n", x, y);
return 0;
}
Pour permettre à une fonction de modifier directement une variable extérieure, il faudra lui transmettre son adresse mémoire à l'aide des pointeurs (ce que nous étudierons en détail dans la Partie 4).
4. Portée et durée de vie des variables (Scope & Lifetime)#
La visibilité et la durée de rétention en mémoire d'une variable dépendent de l'endroit et de la manière dont elle est déclarée :
graph TD
Scope[Portée des Variables]
Scope --> Locales[Variables Locales\nDéclarées dans un bloc { ... }\nVisibles uniquement dans ce bloc\nDétruites à la sortie]
Scope --> Globales[Variables Globales\nDéclarées hors de toute fonction\nVisibles dans tout le fichier\nPrésentes tout au long du programme]
Scope --> Statiques[Variables Statiques locales (static)\nDéclarées dans une fonction\nConservent leur valeur entre chaque appel !]
La variable statique locale (static)#
Le mot-clé static placé devant une variable locale indique au compilateur de ne pas détruire cette variable à la fin de la fonction. Elle conserve sa valeur d'un appel à l'autre :
#include <stdio.h>
void CompterConnexion(void)
{
// Initialisée une seule fois au tout premier appel
static int compteur = 0;
compteur++;
printf("Nombre de visites enregistrees : %d\n", compteur);
}
int main(void)
{
CompterConnexion(); // Affiche 1
CompterConnexion(); // Affiche 2
CompterConnexion(); // Affiche 3
return 0;
}
5. Mise en pratique : Calculateur de mentions académiques#
Énoncé :
Développez un programme modulaire contenant :
1. Une fonction CalculerMoyenne(float n1, float n2, float n3) renvoyant la moyenne arithmétique.
2. Une fonction ObtenirMention(float moyenne) renvoyant un caractère représentant la mention :
- 'T' : Très Bien (\(\ge 16.0\))
- 'B' : Bien (\(\ge 14.0\))
- 'A' : Assez Bien (\(\ge 12.0\))
- 'P' : Passable (\(\ge 10.0\))
- 'E' : Éliminé / Ajourné (\(< 10.0\))
3. Une fonction AfficherDecision(float moyenne, char mention) qui imprime le rapport final.
??? tip "Solution complète"
#include <stdio.h>
// Prototypes
float CalculerMoyenne(float n1, float n2, float n3);
char ObtenirMention(float moyenne);
void AfficherDecision(float moyenne, char mention);
int main(void)
{
float noteMath = 15.5f;
float noteC = 17.0f;
float noteBDD = 13.5f;
float moy = CalculerMoyenne(noteMath, noteC, noteBDD);
char ment = ObtenirMention(moy);
AfficherDecision(moy, ment);
return 0;
}
float CalculerMoyenne(float n1, float n2, float n3)
{
return (n1 + n2 + n3) / 3.0f;
}
char ObtenirMention(float moyenne)
{
if (moyenne >= 16.0f) return 'T';
if (moyenne >= 14.0f) return 'B';
if (moyenne >= 12.0f) return 'A';
if (moyenne >= 10.0f) return 'P';
return 'E';
}
void AfficherDecision(float moyenne, char mention)
{
printf("Moyenne generale : %.2f / 20\n", moyenne);
printf("Mention obtenue : ");
switch (mention)
{
case 'T': printf("Tres Bien\n"); break;
case 'B': printf("Bien\n"); break;
case 'A': printf("Assez Bien\n"); break;
case 'P': printf("Passable\n"); break;
default: printf("Ajourne\n"); break;
}
}
Vous maîtrisez les bases des fonctions. Explorons maintenant un mode d'exécution particulièrement élégant et puissant : la récursivité.
Commentaires