Après avoir exploré les bases de la syntaxe du langage C, des types de données et des opérateurs, nous abordons un pilier fondamental de la programmation : les **structures de contrôle**. En algorithmique, vous avez appris à concevoir des séquences d'instructions, des choix conditionnels et des répétitions. Le langage C, comme tout langage de programmation complet, offre des constructions spécifiques pour traduire ces concepts algorithmiques en code exécutable. Les structures de contrôle sont essentielles car elles permettent de dicter le *flux d'exécution* de votre programme. Sans elles, un programme se contenterait d'exécuter une série d'instructions de manière linéaire, du début à la fin, sans aucune logique décisionnelle ou répétitive. C'est grâce à elles que nos programmes peuvent réagir à des entrées, gérer des erreurs, ou exécuter des algorithmes complexes. > [!definition] Structure de Contrôle > Une **structure de contrôle** est une instruction ou un bloc d'instructions qui modifie l'ordre séquentiel d'exécution des instructions d'un programme. Elle permet de prendre des décisions (branchements conditionnels) ou de répéter des actions (boucles). Dans ce chapitre, nous allons détailler les deux grandes catégories de structures de contrôle en C : 1. **Les branchements conditionnels** (ou conditions) : `if`, `else`, `switch`, qui permettent d'exécuter différents blocs de code en fonction de la véracité de certaines expressions. 2. **Les boucles** : `while`, `for`, `do-while`, ainsi que les instructions `break` et `continue`, qui permettent de répéter l'exécution d'un bloc de code un certain nombre de fois ou tant qu'une condition est vraie. Il vous faut maîtriser ces concepts pour choisir la structure de contrôle la plus appropriée à la résolution d'un problème donné. ## I. Les Branchements Conditionnels : `if`, `else`, `switch` Les branchements conditionnels permettent à votre programme de prendre des décisions. En fonction de la vérité ou de la fausseté d'une ou plusieurs conditions, différentes parties du code seront exécutées. C'est la traduction en C de la structure algorithmique "SI... ALORS... SINON...". ### 1.1. L'instruction `if` L'instruction `if` (si) est la structure de décision la plus simple. Elle exécute un bloc de code *seulement si* une condition spécifiée est vraie. > [!definition] Syntaxe de `if` > ```c > if (condition) { > // Bloc d'instructions à exécuter si la condition est vraie > } > ``` > - La `condition` est une expression évaluée à un entier. En C, toute valeur non nulle est considérée comme `true` (vraie), et `0` est considérée comme `false` (fausse). > - Les accolades `{}` délimitent le bloc d'instructions. Si le bloc ne contient qu'une seule instruction, les accolades sont optionnelles, mais leur utilisation est fortement recommandée pour la lisibilité et pour éviter des erreurs. > [!example] Exemple simple de `if` > ```c > #include <stdio.h> > > int main() { > int age = 20; > > if (age >= 18) { > printf("Vous êtes majeur.\n"); > } > printf("Fin du programme.\n"); > return 0; > } > ``` > **Explication :** La condition `age >= 18` est vraie (20 est supérieur ou égal à 18). Le message "Vous êtes majeur." est donc affiché. Si `age` avait été `16`, rien n'aurait été affiché par le `if`. ### 1.2. L'instruction `if-else` L'instruction `if-else` (si-sinon) permet d'exécuter un bloc de code si la condition est vraie, et un *autre* bloc de code si la condition est fausse. > [!definition] Syntaxe de `if-else` > ```c > if (condition) { > // Bloc d'instructions à exécuter si la condition est vraie > } else { > // Bloc d'instructions à exécuter si la condition est fausse > } > ``` > [!example] Exemple de `if-else` > ```c > #include <stdio.h> > > int main() { > int nombre; > printf("Entrez un nombre entier : "); > scanf("%d", &nombre); > > if (nombre % 2 == 0) { // L'opérateur % donne le reste de la division > printf("%d est un nombre pair.\n", nombre); > } else { > printf("%d est un nombre impair.\n", nombre); > } > return 0; > } > ``` > **Explication :** Si le reste de la division de `nombre` par 2 est 0 (condition `nombre % 2 == 0`), le nombre est pair. Sinon, il est impair. ### 1.3. L'instruction `if-else if-else` (Conditions Imbriquées) Pour gérer plusieurs conditions mutuellement exclusives, on peut chaîner des `if-else if`. C'est une manière élégante de gérer une série de choix. > [!definition] Syntaxe de `if-else if-else` > ```c > if (condition1) { > // Bloc A si condition1 est vraie > } else if (condition2) { > // Bloc B si condition1 est fausse ET condition2 est vraie > } else if (condition3) { > // Bloc C si condition1 et condition2 sont fausses ET condition3 est vraie > } else { > // Bloc D si toutes les conditions précédentes sont fausses > } > ``` > - Le programme teste les conditions dans l'ordre. Dès qu'une condition est vraie, le bloc de code associé est exécuté, et le reste de la chaîne `else if` est ignoré. > - Le bloc `else` final est optionnel et sert de "cas par défaut" si aucune des conditions précédentes n'est vraie. > [!example] Exemple de `if-else if-else` > ```c > #include <stdio.h> > > int main() { > int note; > printf("Entrez la note de l'étudiant (entre 0 et 20) : "); > scanf("%d", &note); > > if (note >= 18) { > printf("Excellent !\n"); > } else if (note >= 14) { > printf("Très bien.\n"); > } else if (note >= 10) { > printf("Admis.\n"); > } else { > printf("Non admis.\n"); > } > return 0; > } > ``` > **Explication :** Si la note est 19, "Excellent !" est affiché et les autres `else if` sont ignorés. Si la note est 12, la première condition (`note >= 18`) est fausse, la deuxième (`note >= 14`) est fausse, la troisième (`note >= 10`) est vraie, donc "Admis." est affiché. > [!note] Bonnes Pratiques > - **Indentation :** Indentez toujours le code à l'intérieur des blocs `if`, `else`, `else if` pour améliorer la lisibilité. > - **Accolades :** Utilisez toujours les accolades, même pour une seule instruction, pour éviter les erreurs logiques courantes (comme l'erreur du "dangling else"). > - **Conditions complexes :** Pour des conditions très complexes, décomposez-les en sous-conditions ou utilisez des variables booléennes pour plus de clarté. ### 1.4. L'instruction `switch` L'instruction `switch` (aiguillage) est une alternative aux chaînes `if-else if` lorsque vous devez comparer une seule variable (ou expression) à plusieurs valeurs constantes. Elle est particulièrement utile pour les menus ou les traitements basés sur des codes d'erreur. > [!definition] Syntaxe de `switch` > ```c > switch (expression) { > case valeur1: > // Instructions si expression == valeur1 > break; // Très important ! > case valeur2: > // Instructions si expression == valeur2 > break; > // ... > default: > // Instructions si expression ne correspond à aucune valeur > break; // Optionnel pour le dernier cas > } > ``` > - L'`expression` doit être de type entier (ou un type qui peut être implicitement converti en entier, comme `char`). > - Chaque `case` doit être suivi d'une *valeur constante* unique. > - L'instruction `break;` est cruciale : elle permet de sortir du bloc `switch` une fois qu'un `case` correspondant a été traité. > - Le bloc `default:` est optionnel et est exécuté si l'`expression` ne correspond à aucune des `valeur` des `case` précédents. > [!example] Exemple de `switch` > ```c > #include <stdio.h> > > int main() { > char operation; > double num1, num2, resultat; > > printf("Entrez l'opérateur (+, -, *, /) : "); > scanf(" %c", &operation); // L'espace avant %c est important pour ignorer les blancs > > printf("Entrez deux nombres : "); > scanf("%lf %lf", &num1, &num2); > > switch (operation) { > case '+': > resultat = num1 + num2; > printf("Résultat : %.2lf\n", resultat); > break; > case '-': > resultat = num1 - num2; > printf("Résultat : %.2lf\n", resultat); > break; > case '*': > resultat = num1 * num2; > printf("Résultat : %.2lf\n", resultat); > break; > case '/': > if (num2 != 0) { > resultat = num1 / num2; > printf("Résultat : %.2lf\n", resultat); > } else { > printf("Erreur : Division par zéro !\n"); > } > break; > default: > printf("Opérateur invalide.\n"); > break; > } > return 0; > } > ``` > [!warning] Attention au `break` manquant (Fall-through) > Si vous oubliez un `break;` après un `case`, l'exécution "tombera" dans le `case` suivant et exécutera ses instructions, et ainsi de suite, jusqu'à rencontrer un `break` ou la fin du `switch`. Cela peut être voulu dans certains cas (par exemple, pour regrouper plusieurs `case` qui exécutent le même code), mais c'est le plus souvent une erreur logique. > ```c > int jour = 1; > switch (jour) { > case 1: > printf("Lundi\n"); > case 2: // Oups, pas de break ! > printf("Mardi\n"); > break; > default: > printf("Autre jour\n"); > break; > } > // Si jour = 1, affichera "Lundi" puis "Mardi" > ``` > [!note] `switch` vs `if-else if` > - Utilisez `switch` lorsque vous comparez une *seule expression entière* à plusieurs *valeurs constantes*. C'est souvent plus lisible et potentiellement plus optimisé par le compilateur. > - Utilisez `if-else if` pour des conditions plus complexes (par exemple, des plages de valeurs, des comparaisons de chaînes de caractères, ou des combinaisons de plusieurs variables). --- ## II. Les Boucles : `while`, `for`, `do-while`, `break`, `continue` Les boucles sont des structures de contrôle qui permettent de répéter un bloc d'instructions plusieurs fois. C'est le fondement de l'automatisation en programmation. ### 2.1. La boucle `while` La boucle `while` (tant que) répète un bloc d'instructions *tant qu'une condition est vraie*. La condition est testée *avant chaque itération*. > [!definition] Syntaxe de `while` > ```c > while (condition) { > // Bloc d'instructions à répéter > // Assurez-vous que la condition puisse devenir fausse à un moment donné ! > } > ``` > - Si la `condition` est fausse dès le départ, le bloc d'instructions ne sera jamais exécuté. > [!example] Exemple de `while` : Compteur > ```c > #include <stdio.h> > > int main() { > int compteur = 0; > while (compteur < 5) { > printf("Compteur : %d\n", compteur); > compteur++; // Incrémentation du compteur > } > printf("Boucle terminée. Compteur final : %d\n", compteur); > return 0; > } > ``` > **Explication :** La boucle s'exécute tant que `compteur` est inférieur à 5. À chaque itération, `compteur` est affiché puis incrémenté. Quand `compteur` atteint 5, la condition `compteur < 5` devient fausse, et la boucle s'arrête. > [!warning] Boucle Infinie > Si la condition d'une boucle `while` ne devient jamais fausse, la boucle s'exécutera indéfiniment, bloquant votre programme. Assurez-vous toujours qu'il y a un moyen de sortir de la boucle. > ```c > // Exemple de boucle infinie (à éviter !) > // int i = 0; > // while (i < 5) { > // printf("%d\n", i); > // // Oubli de i++; > // } > ``` ### 2.2. La boucle `for` La boucle `for` (pour) est généralement utilisée lorsque le nombre d'itérations est connu ou facilement déterminable à l'avance. Elle regroupe l'initialisation, la condition et l'incrémentation (ou décrémentation) du compteur de boucle en une seule ligne. > [!definition] Syntaxe de `for` > ```c > for (initialisation; condition; incrémentation) { > // Bloc d'instructions à répéter > } > ``` > - **`initialisation` :** Exécutée une seule fois au début de la boucle. Souvent utilisée pour déclarer et initialiser une variable de contrôle. > - **`condition` :** Évaluée avant chaque itération. Si elle est vraie, le corps de la boucle est exécuté. Si elle est fausse, la boucle se termine. > - **`incrémentation` :** Exécutée après chaque itération du corps de la boucle. Souvent utilisée pour modifier la variable de contrôle. > [!example] Exemple de `for` : Somme des N premiers entiers > ```c > #include <stdio.h> > > int main() { > int n = 10; > int somme = 0; > int i; // Déclaration de la variable de boucle > > for (i = 1; i <= n; i++) { > somme += i; // Équivalent à somme = somme + i; > } > printf("La somme des %d premiers entiers est : %d\n", n, somme); > return 0; > } > ``` > **Explication :** > 1. `i = 1;` : `i` est initialisé à 1. > 2. `i <= n;` : La condition est vérifiée (1 <= 10 est vrai). > 3. Le corps de la boucle est exécuté (`somme = 0 + 1`). > 4. `i++` : `i` est incrémenté (devient 2). > 5. Retour à l'étape 2. Ce processus se répète jusqu'à ce que `i` soit 11, rendant la condition fausse. > [!tip] La boucle `for` est idéale pour parcourir des collections (comme les tableaux, que nous verrons plus tard) ou pour des tâches où le nombre d'itérations est prévisible. C'est un outil essentiel pour l'initialisation de structures de données ou pour des calculs répétitifs en robotique, par exemple. ### 2.3. La boucle `do-while` La boucle `do-while` (faire-tant que) est similaire à `while`, mais la condition est testée *après* la première exécution du bloc d'instructions. Cela garantit que le corps de la boucle est exécuté *au moins une fois*. > [!definition] Syntaxe de `do-while` > ```c > do { > // Bloc d'instructions à répéter > } while (condition); // N'oubliez pas le point-virgule ! > ``` > [!example] Exemple de `do-while` : Saisie utilisateur avec validation > ```c > #include <stdio.h> > > int main() { > int choix; > do { > printf("--- Menu ---\n"); > printf("1. Jouer\n"); > printf("2. Options\n"); > printf("3. Quitter\n"); > printf("Votre choix : "); > scanf("%d", &choix); > > if (choix < 1 || choix > 3) { > printf("Choix invalide. Veuillez réessayer.\n"); > } > > } while (choix < 1 || choix > 3); // La boucle continue tant que le choix est invalide > > printf("Vous avez choisi l'option %d.\n", choix); > return 0; > } > ``` > **Explication :** Le menu est affiché et le choix est demandé au moins une fois. Si le `choix` est en dehors de l'intervalle [1, 3], la condition `choix < 1 || choix > 3` est vraie, et la boucle se répète. Dès qu'un choix valide est entré, la condition devient fausse et la boucle se termine. > [!note] `while` vs `do-while` > - Utilisez `while` lorsque le bloc de code peut ne pas avoir besoin d'être exécuté du tout (condition testée avant). > - Utilisez `do-while` lorsque le bloc de code doit être exécuté au moins une fois (condition testée après). ### 2.4. Les instructions de contrôle de boucles : `break` et `continue` Ces deux instructions permettent de modifier le comportement normal des boucles. #### 2.4.1. L'instruction `break` L'instruction `break` permet de sortir *immédiatement* de la boucle la plus proche (qu'elle soit `for`, `while` ou `do-while`). L'exécution du programme reprendra à l'instruction qui suit la boucle. > [!example] Exemple de `break` : Recherche d'un nombre > ```c > #include <stdio.h> > > int main() { > int nombres[] = {10, 25, 30, 42, 55, 60}; // Un tableau d'entiers > int taille = sizeof(nombres) / sizeof(nombres[0]); > int cible = 42; > int trouve = 0; // Indicateur pour savoir si l'élément a été trouvé > int i; > > for (i = 0; i < taille; i++) { > if (nombres[i] == cible) { > printf("Le nombre %d a été trouvé à l'indice %d.\n", cible, i); > trouve = 1; > break; // Sortir de la boucle dès que l'élément est trouvé > } > } > > if (!trouve) { // Si trouve est 0 (faux) > printf("Le nombre %d n'a pas été trouvé dans le tableau.\n", cible); > } > return 0; > } > ``` > **Explication :** Dès que `cible` est trouvé, `break` arrête la boucle `for`, même si `i` n'a pas encore atteint `taille - 1`. Cela est plus efficace que de continuer à parcourir le reste du tableau. #### 2.4.2. L'instruction `continue` L'instruction `continue` permet de passer *immédiatement à l'itération suivante* de la boucle la plus proche. Le code restant dans l'itération actuelle est ignoré, et la boucle passe à l'évaluation de sa condition (pour `while` et `for`) ou à l'incrémentation (pour `for`). > [!example] Exemple de `continue` : Afficher les nombres pairs > ```c > #include <stdio.h> > > int main() { > int i; > printf("Nombres pairs de 1 à 10 :\n"); > for (i = 1; i <= 10; i++) { > if (i % 2 != 0) { // Si le nombre est impair > continue; // Passer à l'itération suivante > } > printf("%d ", i); // Cette ligne n'est exécutée que pour les nombres pairs > } > printf("\n"); > return 0; > } > ``` > **Explication :** Si `i` est impair, `continue` est exécuté, et le `printf` est ignoré pour cette itération. La boucle passe directement à `i++` pour l'itération suivante. > [!note] Utilisation judicieuse de `break` et `continue` > Bien que puissantes, `break` et `continue` peuvent rendre le code plus difficile à lire et à déboguer si elles sont utilisées de manière excessive ou inappropriée. Préférez des conditions de boucle claires lorsque c'est possible. Elles sont souvent très utiles dans des cas spécifiques, comme la sortie anticipée d'une recherche ou le filtrage d'éléments. --- ## III. Application et Bonnes Pratiques La maîtrise des structures de contrôle est fondamentale. C'est avec elles que vous allez traduire la logique de vos algorithmes en programmes C fonctionnels. ### Importance pour les Compétences Futures - **Pointeurs :** Lorsque vous manipulerez des pointeurs pour parcourir des tableaux ou des structures de données dynamiques, les boucles (`for` et `while`) seront vos outils principaux. Comprendre comment contrôler précisément ces boucles sera essentiel pour éviter les erreurs d'accès mémoire. - **Robotique :** En robotique, les structures de contrôle sont omniprésentes. Un robot doit prendre des décisions (aller à gauche si un obstacle est détecté, sinon aller tout droit) et répéter des actions (avancer, lire des capteurs, ajuster sa position) en permanence. Votre code C pour contrôler un robot sera rempli de `if-else` et de boucles. ### Bonnes Pratiques de Codage - **Lisibilité :** Un code bien structuré avec une indentation cohérente est plus facile à comprendre et à maintenir. - **Clarté des conditions :** Évitez les conditions trop complexes. Si une condition devient illisible, décomposez-la en plusieurs étapes ou utilisez des variables intermédiaires. - **Commentaires :** Expliquez les parties complexes de votre logique, surtout si vous utilisez `break` ou `continue` de manière non triviale. - **Testez vos boucles :** Assurez-vous que vos boucles se terminent toujours et qu'elles gèrent correctement les cas limites (par exemple, une boucle `for` avec 0 itération, ou une boucle `while` dont la condition est fausse dès le départ). --- ## Conclusion Nous avons couvert les structures de contrôle essentielles en C : - **Branchements conditionnels :** `if`, `if-else`, `if-else if-else` pour les décisions binaires ou multiples, et `switch` pour les comparaisons d'une expression avec des valeurs constantes. - **Boucles :** `while` pour les répétitions conditionnelles (test avant), `for` pour les répétitions avec un nombre d'itérations connu (compteur), et `do-while` pour garantir au moins une exécution (test après). - **Contrôle de boucles :** `break` pour sortir prématurément d'une boucle, et `continue` pour passer à l'itération suivante. La maîtrise de ces outils est non seulement un pré-requis pour les chapitres à venir (notamment sur les tableaux et les pointeurs), mais aussi une compétence fondamentale pour tout ingénieur développeur. Elles vous permettront de créer des programmes dynamiques, réactifs et capables d'interagir intelligemment avec leur environnement. Entraînez-vous abondamment avec ces structures, elles sont la base de toute logique de programme complexe.