Apprendre à programmer

Fonction récursive en C : Exercices Corrigés

×

Recommandés

Récursion en C pour débutants guide pratique et exercices corrigés

La programmation récursive est une technique où une fonction s’appelle elle-même pour résoudre un problème. Cette approche est particulièrement utile pour les problèmes qui peuvent être décomposés en sous-problèmes similaires. En langage C, la récursivité est un concept puissant mais doit être utilisé avec précaution pour éviter les erreurs telles que les débordements de pile.

Structure d’une fonction récursive

Pour qu’une fonction récursive soit correctement implémentée, elle doit comporter deux éléments clés :

  1. Cas de base : une condition qui met fin à la récursivité.
  2. Appel récursif : la fonction s’appelle elle-même avec des arguments modifiés, rapprochant ainsi la solution du cas de base.

Exemple de base : Factorielle

La fonction factorielle d’un nombre ( n ) (notée ( n! )) est un exemple classique de récursion. Voici comment on peut l’implémenter en C :

#include <stdio.h>

int factorielle(int n) {
    if (n <= 1) {
        return 1;
    } else {
        return n * factorielle(n - 1);
    }
}

int main() {
    int nombre = 5;
    printf("La factorielle de %d est %d\n", nombre, factorielle(nombre));
    return 0;
}

Exercices Corrigés en langage C : Fonction récursive

Voici une série de 16 exercices corrigés pour approfondir la compréhension des fonctions récursives en C.

Exercice 1 : Somme des entiers de 1 à n

Énoncé : Écrire une fonction récursive qui calcule la somme des entiers de 1 à ( n ).

Correction :

#include <stdio.h>

int somme(int n) {
    if (n == 1) {
        return 1;
    } else {
        return n + somme(n - 1);
    }
}

int main() {
    int nombre = 10;
    printf("La somme des entiers de 1 à %d est %d\n", nombre, somme(nombre));
    return 0;
}

Exercice 2 : Série de Fibonacci

Énoncé : Écrire une fonction récursive pour générer le ( n )-ième terme de la série de Fibonacci.

Correction :

#include <stdio.h>

int fibonacci(int n) {
    if (n <= 1) {
        return n;
    } else {
        return fibonacci(n - 1) + fibonacci(n - 2);
    }
}

int main() {
    int terme = 6;
    printf("Le %d-ième terme de la série de Fibonacci est %d\n", terme, fibonacci(terme));
    return 0;
}

Exercice 3 : Calcul de la puissance

Énoncé : Écrire une fonction récursive pour calculer ( x ) élevé à la puissance ( y ) ( ( x^y ) ).

Correction :

#include <stdio.h>

int puissance(int x, int y) {
    if (y == 0) {
        return 1;
    } else {
        return x * puissance(x, y - 1);
    }
}

int main() {
    int base = 2, exposant = 3;
    printf("%d à la puissance %d est %d\n", base, exposant, puissance(base, exposant));
    return 0;
}
Exercice 4 : Inverser une chaîne de caractères

Énoncé : Écrire une fonction récursive pour inverser une chaîne de caractères.

Correction :

#include <stdio.h>
#include <string.h>

void inverser(char *str, int debut, int fin) {
    if (debut >= fin) {
        return;
    }
    char temp = str[debut];
    str[debut] = str[fin];
    str[fin] = temp;
    inverser(str, debut + 1, fin - 1);
}

int main() {
    char chaine[] = "recursion";
    inverser(chaine, 0, strlen(chaine) - 1);
    printf("Chaîne inversée : %s\n", chaine);
    return 0;
}

Exercice 5 : Nombre de chiffres

Énoncé : Écrire une fonction récursive pour compter le nombre de chiffres d’un entier.

Correction :

#include <stdio.h>

int compte_chiffres(int n) {
    if (n == 0) {
        return 0;
    } else {
        return 1 + compte_chiffres(n / 10);
    }
}

int main() {
    int nombre = 12345;
    printf("Le nombre de chiffres dans %d est %d\n", nombre, compte_chiffres(nombre));
    return 0;
}

Exercice 6 : Somme des chiffres

Énoncé : Écrire une fonction récursive pour calculer la somme des chiffres d’un entier.

Correction :

#include <stdio.h>

int somme_chiffres(int n) {
    if (n == 0) {
        return 0;
    } else {
        return (n % 10) + somme_chiffres(n / 10);
    }
}

int main() {
    int nombre = 12345;
    printf("La somme des chiffres de %d est %d\n", nombre, somme_chiffres(nombre));
    return 0;
}

Exercice 7 : Recherche d’un élément dans un tableau

Énoncé : Écrire une fonction récursive pour rechercher un élément dans un tableau.

Correction :

#include <stdio.h>

int recherche(int arr[], int taille, int element) {
    if (taille == 0) {
        return -1;
    } else if (arr[taille - 1] == element) {
        return taille - 1;
    } else {
        return recherche(arr, taille - 1, element);
    }
}

int main() {
    int tableau[] = {2, 4, 6, 8, 10};
    int taille = sizeof(tableau) / sizeof(tableau[0]);
    int element = 6;
    int index = recherche(tableau, taille, element);
    if (index != -1) {
        printf("Élément %d trouvé à l'index %d\n", element, index);
    } else {
        printf("Élément %d non trouvé\n", element);
    }
    return 0;
}

Exercice 8 : Calcul de la somme des éléments d’un tableau

Énoncé : Écrire une fonction récursive pour calculer la somme des éléments d’un tableau.

Correction :

#include <stdio.h>

int somme_tableau(int arr[], int taille) {
    if (taille == 0) {
        return 0;
    } else {
        return arr[taille - 1] + somme_tableau(arr, taille - 1);
    }
}

int main() {
    int tableau[] = {2, 4, 6, 8, 10};
    int taille = sizeof(tableau) / sizeof(tableau[0]);
    printf("La somme des éléments du tableau est %d\n", somme_tableau(tableau, taille));
    return 0;
}
Exercice 9 : Trouver le maximum dans un tableau

Énoncé : Écrire une fonction récursive pour trouver le maximum dans un tableau.

Correction :

#include <stdio.h>

int maximum(int arr[], int taille) {
    if (taille == 1) {
        return arr[0];
    } else {
        int max = maximum(arr, taille - 1);
        return (arr[taille - 1] > max) ? arr[taille - 1] : max;
    }
}

int main() {
    int tableau[] = {2, 4, 6, 8, 10};
    int taille = sizeof(tableau) / sizeof(tableau[0]);
    printf("Le maximum du tableau est %d\n", maximum(tableau, taille));
    return 0;
}

Exercice 10 : Tri à bulle récursif

Énoncé : Écrire une fonction récursive pour trier un tableau en utilisant le tri à bulle.

Correction :

#include <stdio.h>

void echanger(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

void tri_bulle(int arr[], int taille) {
    if (taille == 1) {
        return;
    }
    for (int i = 0; i < taille - 1; i++) {
        if (arr[i] > arr[i + 1]) {
            echanger(&arr[i], &arr[i + 1]);
        }
    }
    tri_bulle(arr, taille - 1);
}

int main() {
    int tableau[] = {10, 9, 8, 7, 6};
    int taille = sizeof(tableau) / sizeof(tableau[0]);
    tri_bulle(tableau, taille);
    printf("Tableau trié : ");
    for (int i = 0; i < taille; i++) {
        printf("%d ", tableau[i]);
    }
    printf("\n");
    return 0;
}

Exercice 11 : Convertir un entier en binaire

Énoncé : Écrire une fonction récursive pour convertir un entier en binaire.

Correction :

#include <stdio.h>

void convertir_binaire(int n) {
    if (n == 0) {
        return;
    }
    convertir_binaire(n / 2);
    printf("%d", n % 2);
}

int main() {
    int nombre = 10;
    printf("Le binaire de %d est ", nombre);
    convertir_binaire(nombre);
    printf("\n");
    return 0;
}

Exercice 12 : Calcul de la moyenne des éléments d’un tableau

Énoncé : Écrire une fonction récursive pour calculer la moyenne des éléments d’un tableau.

Correction :

#include <stdio.h>

float somme_elements(float arr[], int taille) {
    if (taille == 0) {
        return 0;
    } else {
        return arr[taille - 1] + somme_elements(arr, taille - 1);
    }
}

float moyenne(float arr[], int taille) {
    return somme_elements(arr, taille) / taille;
}

int main() {
    float tableau[] = {2.0, 4.0, 6.0, 8.0, 10.0};
    int taille = sizeof(tableau) / sizeof(tableau[0]);
    printf("La moyenne des éléments du tableau est %.2f\n", moyenne(tableau, taille));
    return 0;
}

Exercice 13 : Trouver le nombre d’occurrences d’un élément dans un tableau

Énoncé : Écrire une fonction récursive pour trouver le nombre d’occurrences d’un élément dans un tableau.

Correction :

#include <stdio.h>

int occurrences(int arr[], int taille, int element) {
    if (taille == 0) {
        return 0;
    } else {
        int count = (arr[taille - 1] == element) ? 1 : 0;
        return count + occurrences(arr, taille - 1, element);
    }
}

int main() {
    int tableau[] = {2, 4, 6, 8, 4, 10};
    int taille = sizeof(tableau) / sizeof(tableau[0]);
    int element = 4;
    printf("Le nombre d'occurrences de %d est %d\n", element, occurrences(tableau, taille, element));
    return 0;
}

Exercice 14 : Trouver la somme des éléments pairs d’un tableau

Énoncé : Écrire une fonction récursive pour trouver la somme des éléments pairs d’un tableau.

Correction :

#include <stdio.h>

int somme_pairs(int arr[], int taille) {
    if (taille == 0) {
        return 0;
    } else {
        int sum = (arr[taille - 1] % 2 == 0) ? arr[taille - 1] : 0;
        return sum + somme_pairs(arr, taille - 1);
    }
}

int main() {
    int tableau[] = {2, 4, 6, 8, 10};
    int taille = sizeof(tableau) / sizeof(tableau[0]);
    printf("La somme des éléments pairs du tableau est %d\n", somme_pairs(tableau, taille));
    return 0;
}

Exercice 15 : Trouver la profondeur maximale d’un arbre binaire

Énoncé : Écrire une fonction récursive pour trouver la profondeur maximale d’un arbre binaire.

Correction :

#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node* left;
    struct Node* right;
};

int profondeur_max(struct Node* node) {
    if (node == NULL) {
        return 0;
    } else {
        int left_depth = profondeur_max(node->left);
        int right_depth = profondeur_max(node->right);
        return (left_depth > right_depth) ? (left_depth + 1) : (right_depth + 1);
    }
}

struct Node* nouveauNoeud(int data) {
    struct Node* node = (struct Node*)malloc(sizeof(struct Node));
    node->data = data;
    node->left = NULL;
    node->right = NULL;
    return node;
}

int main() {
    struct Node* root = nouveauNoeud(1);
    root->left = nouveauNoeud(2);
    root->right = nouveauNoeud(3);
    root->left->left = nouveauNoeud(4);
    root->left->right = nouveauNoeud(5);
    printf("La profondeur maximale de l'arbre binaire est %d\n", profondeur_max(root));
    return 0;
}
Exercice 16 : Vérifier si une chaîne est un palindrome

Énoncé : Écrire une fonction récursive pour vérifier si une chaîne est un palindrome.

Correction :

#include <stdio.h>
#include <string.h>
#include <stdbool.h>

bool est_palindrome(char *str, int debut, int fin) {
    if (debut >= fin) {
        return true;
    }
    if (str[debut] != str[fin]) {
        return false;
    }
    return est_palindrome(str, debut + 1, fin - 1);
}

int main() {
    char chaine[] = "radar";
    if (est_palindrome(chaine, 0, strlen(chaine) - 1)) {
        printf("%s est un palindrome\n", chaine);
    } else {
        printf("%s n'est pas un palindrome\n", chaine);
    }
    return 0;
}

Les fonctions récursives offrent une méthode élégante et efficace pour résoudre divers problèmes de programmation. Bien que la récursion puisse être plus intuitive pour certains problèmes, il est important de comprendre ses limitations, notamment en termes de consommation de mémoire et de temps d’exécution. Les exercices corrigés ci-dessus devraient fournir une base solide pour approfondir la maîtrise de la récursion en C.


Encadré Méthode – Guide rapide pour comprendre la récursion en C

Cet encadré est un outil d’aide visuelle conçu pour les développeurs débutants qui souhaitent maîtriser la récursion en C sans se perdre dans des explications trop théoriques.
Il agit comme une mini-feuille de route intégrée directement dans la page, toujours visible et consultable pendant que l’on code.


À quoi sert cet encadré ?

Son utilité principale est de transformer un concept souvent abstrait en étapes concrètes et applicables immédiatement.
Au lieu de relire tout un chapitre ou de chercher ailleurs, l’utilisateur dispose d’un repère clair et synthétique qui lui rappelle :

  • où s’arrête la fonction récursive
  • comment réduire progressivement le problème
  • quelle structure de code utiliser
  • quels pièges éviter
  • quoi vérifier avant de valider son programme

C’est à la fois un mémo, un guide de méthode, et un outil de sécurisation du raisonnement.


Pourquoi il est utile pour un dev junior

Pour un débutant, la récursion peut sembler déroutante. Cet encadré :

  • évite la confusion entre boucle et récursion
  • réduit le risque de récursion infinie
  • structure la réflexion avant même d’écrire le code
  • favorise l’autonomie sans dépendre constamment d’un tutoriel externe
  • renforce la confiance lors du débogage

En pratique, il sert de check-list intelligente que l’on consulte rapidement avant d’exécuter son programme.


Ce que l’utilisateur y gagne

  • Gain de temps : plus besoin de chercher la méthode ailleurs
  • Clarté : une vision globale en quelques secondes
  • Sécurité : moins d’erreurs logiques
  • Progression rapide : compréhension accélérée du mécanisme récursif
  • Mémoire visuelle : le design type terminal aide à retenir la structure

Il s’agit d’un outil pédagogique actif qui accompagne l’utilisateur pendant l’apprentissage, l’écriture et la vérification du code.
Il transforme une notion complexe en processus simple, visuel et répétable, exactement ce dont un développeur junior a besoin pour passer de la théorie à la pratique avec assurance.

terminal://recursion-c/methode-dev-junior
C • Recursion • Debug mode

🧠 Méthode rapide pour coder une fonction récursive en C

Objectif : écrire une récursion qui termine, qui réduit le problème à chaque appel, et qui reste lisible au débogage.

1️⃣ Définis la règle d’arrêt

  1. Écris la condition qui stoppe les appels (cas de base).
  2. Décide la valeur retournée dans ce cas (souvent 0, 1, ou un état “neutre”).
  3. Teste mentalement 2 cas simples : n=0 et n=1.
Check : “Si je supprime la récursion, est-ce que mon cas de base suffit à donner une réponse ?”

2️⃣ Réduction du problème

  1. Choisis une transformation qui rapproche du cas de base : n → n-1, p → p/10, liste → liste->next
  2. Assure-toi que la transformation ne peut pas “rester bloquée”.
  3. Écris la relation : f(n) = … f(n-1) …
⚙️ Astuce : si tu ne sais pas réduire, écris d’abord la version itérative, puis “plie-la” en récursion.

3️⃣ Schéma universel de récursion

Tu peux partir de ce gabarit et remplacer les éléments en gras.

// f : résout le problème pour "state"
int f(int state) {
    // (A) cas de base : STOP
    if (/* condition d'arrêt */) {
        return /* valeur de base */;
    }

    // (B) réduction : state -> smaller_state
    int smaller_state = /* transformation */;

    // (C) appel récursif
    int sub = f(smaller_state);

    // (D) combinaison : reconstruire la réponse
    return /* combine(state, sub) */;
}

4️⃣ Debug comme un hacker

  • Ajoute un log minimal : printf(« n=%d\n », n);
  • Indentation dynamique : afficher des espaces selon la profondeur.
  • Stoppe sur un cas précis : si n == 7 alors print + return.
// mini trace de profondeur
int f(int n, int depth){
    for(int i=0;i n=%d\n", n);

    if(n<=1) return 1;
    return n * f(n-1, depth+1);
}

5️⃣ Pièges classiques

☠️ Récursion infinie : cas de base absent ou réduction incorrecte (n ne diminue jamais).
  • Confondre pré-traitement et post-traitement (avant/après l’appel).
  • Oublier les cas négatifs (ex : n < 0).
  • Explosion de complexité (ex : Fibonacci naïf) → memoization ou itération.
  • Stack overflow si la profondeur peut être énorme → version itérative.

Mini checklist avant validation

  • Cas de base existe et couvre tous les scénarios limites
  • À chaque appel, le problème se réduit vraiment
  • La valeur retournée est correcte dans le cas de base
  • La combinaison reconstruit la réponse finale
  • Tests rapides : petit input, input limite, input “bizarre”

setup://c-environment/free-tools
Gratuit • Téléchargeable • Dev Junior Friendly

Installer un environnement C (en 10 minutes)

Choisis ton système. Chaque option ci-dessous te donne un compilateur + (optionnel) un éditeur pour coder proprement. Objectif : pouvoir lancer gcc ou cl et compiler ton premier hello.c.

🪟 Windows 2 options solides
  • Option A (recommandée) : GCC via MSYS2 (rapide + moderne).
  • Option B : Visual Studio Community (MSVC, très standard pro).
// Test rapide (après install)
// 1) Vérifie le compilateur
gcc --version
// 2) Compile
gcc hello.c -o hello
// 3) Exécute
./hello
🍎 macOS simple et propre
  • Installe d’abord les Command Line Tools (compilo + outils).
  • Optionnel : installe Homebrew pour gérer gcc/clang et outils.
// Command Line Tools (terminal)
xcode-select --install

// Vérifie
clang --version
🐧 Linux ultra rapide
  • Sur Ubuntu/Debian : installe build-essential (gcc + make).
  • Ensuite tu compiles directement depuis le terminal.
// Ubuntu / Debian
sudo apt-get update
sudo apt-get install build-essential

gcc --version

Éditeur gratuit (optionnel mais confortable)

Si tu veux une interface propre (syntax highlight, terminal intégré, extensions), utilise VS Code.

Alternative “Clang sur Windows” (pour les curieux)

LLVM-MinGW fournit une toolchain Clang prête à dézipper (pratique si tu veux tester Clang/LLD).


FAQ récursion en C pour dev juniors

Questions qu’on se pose vraiment quand on apprend la récursion, avec des réponses simples, pratiques et orientées code.

Blanc & Bleu
Q C’est quoi la différence entre récursion et boucle ?
Une boucle répète des instructions dans une même fonction, alors qu’une récursion consiste à rappeler la même fonction avec un problème plus petit.
  • Boucle : contrôle par for/while.
  • Récursion : contrôle par cas de base + réduction du problème.
En pratique, certaines solutions sont plus naturelles en récursion (arbres, parcours de listes, backtracking).
Q Comment savoir si ma récursion va se terminer ?
Pose-toi deux questions :
  • Est-ce que j’ai un cas de base qui stoppe les appels ?
  • À chaque appel, est-ce que je rapproche mon entrée du cas de base (ex : n = n - 1) ?
Si la valeur ne diminue jamais (ou peut repartir dans l’autre sens), tu risques une récursion infinie.
Q Pourquoi j’ai un stack overflow (ou mon programme plante) ?
Parce que chaque appel récursif utilise de la mémoire sur la pile (stack). Si la profondeur devient trop grande, ça déborde.
  • Réduis la profondeur (meilleure réduction du problème).
  • Passe en version itérative si la profondeur peut exploser.
  • Évite les récursions inutiles (ex : Fibonacci naïf).
Q Comment déboguer une fonction récursive sans devenir fou ?
La méthode la plus simple : afficher la valeur d’entrée à chaque appel.
  • Ajoute printf("n=%d\n", n); au début de la fonction.
  • Option : affiche une indentation (profondeur) pour voir la “descente” et la “remontée”.
  • Teste avec de petits inputs : 0, 1, 2, 5.
Tu visualises vite si le cas de base est atteint.
Q Je dois faire le calcul avant ou après l’appel récursif ?
Ça dépend de l’objectif :
  • Avant l’appel : utile quand tu veux traiter en “descendant” (pré-traitement).
  • Après l’appel : utile quand tu as besoin du résultat du sous-problème pour combiner (post-traitement).
Exemple classique : factorielle → calcul souvent en post-traitement (on multiplie avec le retour).
Q Quand la récursion est-elle meilleure qu’une boucle ?
Quand la structure du problème est naturellement “emboîtée” :
  • parcours d’un arbre (dossiers/fichiers, expressions)
  • algorithmes de division (tri fusion, quicksort)
  • backtracking (labyrinthe, combinaisons)
Si c’est une répétition simple, une boucle reste souvent plus performante et plus sûre.
Q Pourquoi Fibonacci récursif est lent et comment l’améliorer ?
Le Fibonacci naïf recalcule les mêmes valeurs plusieurs fois (explosion du nombre d’appels).
  • Solution 1 : mémoïsation (tableau qui stocke les résultats).
  • Solution 2 : version itérative (souvent la plus simple et rapide).
Moralité : la récursion est élégante, mais la complexité compte.
Q Quels tests minimum faire avant de valider un exercice récursif ?
Fais au moins :
  • un cas de base (ex : n=0 ou n=1)
  • un petit cas (ex : n=3)
  • un cas “plus grand” (ex : n=10)
  • un cas limite si possible (ex : valeur négative / entrée vide)
Si tous passent, tu as déjà une bonne confiance.
Astuce dev junior : si tu bloques, écris d’abord la version itérative, puis transforme-la en récursion. Et si tu veux compiler rapidement, utilise ton bloc “Installer un environnement C (en 10 minutes)”.

Mini-projets en C pour maîtriser la récursion pas à pas

La récursion est souvent perçue comme abstraite jusqu’au moment où on l’applique à des situations concrètes. Cette page propose une approche orientée pratique : de petits mini-projets progressifs qui transforment la théorie en réflexe. L’objectif est simple : comprendre le cas de base, la réduction du problème et le moment du traitement.

Règle d’or : si vous ne savez pas dire où votre fonction s’arrête et comment elle réduit l’entrée, la récursion risque de tourner en boucle.

Table des mini-projets


Mini-projet 1 — Afficher un nombre en binaire

Objectif : comprendre le traitement après l’appel récursif.

void print_binary(unsigned int n){
 if(n<2){ printf("%u",n); return; }
 print_binary(n/2);
 printf("%u",n%2);
}

Exercice corrigé

Modifier la fonction pour afficher un espace tous les 4 bits.

---

Mini-projet 2 — Somme des chiffres

int sum_digits(int n){
 if(n<0) n=-n;
 if(n<10) return n;
 return (n%10)+sum_digits(n/10);
}

Exercice corrigé

Adapter la fonction pour compter le nombre de chiffres.

---

Mini-projet 3 — Parcours d’un tableau

void print_array(int *a,int n,int i){
 if(i>=n) return;
 printf("%d ",a[i]);
 print_array(a,n,i+1);
}

Exercice corrigé

Retourner la valeur maximale du tableau.

---

Mini-projet 4 — Permutations

void permute(char*s,int l,int r){
 if(l==r){ puts(s); return; }
 for(int i=l;i<=r;i++){
  swap(&s[l],&s[i]);
  permute(s,l+1,r);
  swap(&s[l],&s[i]);
 }
}

Exercice corrigé

Afficher uniquement les permutations commençant par une voyelle.

---

Installer rapidement un environnement C

Windows : MSYS2 ou Visual Studio Community macOS : Command Line Tools Linux : build-essential
---

FAQ Dev Junior

Différence récursion / boucle ?

La boucle répète, la récursion se rappelle elle-même.

Pourquoi stack overflow ?

Profondeur trop grande ou absence de cas de base.

Quand préférer une boucle ?

Quand la structure du problème est linéaire.

Recommandés

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

error: Content is protected !!