Le langage C offre une gamme d'opérateurs pour manipuler les données et des structures de contrôle pour orchestrer le déroulement des programmes. Comprendre ces concepts est essentiel pour écrire des applications robustes et efficaces.
Opérateurs en C
Opérateurs d'Affectation
Les opérateurs d'affectation modifient la valeur de l'opérande de gauche. Ils présentent un effet de bord sur cette variable.
- L'opérande de gauche doit être une variable.
- L'opérande de droite peut être une constante, une autre variable ou une expression.
En plus de l'affectation simple (=), C propose des opérateurs d'affectation composés, qui sont des raccourcis pratiques :
int compteur = 5;
compteur += 3; // Équivalent à compteur = compteur + 3; compteur vaut maintenant 8.
compteur *= 2; // Équivalent à compteur = compteur * 2; compteur vaut maintenant 16.
Les affectations s'effectuent de droite à gauche. Cela permet des chaînes d'affectations :
int val_a, val_b, val_c;
val_a = val_b = val_c = 100; // Affecte 100 à val_c, puis la valeur de val_c à val_b, puis à val_a.
Exemple de conversion d'unités de stockage utilisant l'opérateur de décalage binaire pour la multiplication par des puissances de 2 :
unsigned long capacite_go = 2; // 2 Gigaoctets
unsigned long capacite_octets = capacite_go << 10 << 10 << 10;
// Une façon plus directe serait :
// unsigned long capacite_octets = capacite_go << 30;
// Note : 1 Go = 2^10 Mo = 2^20 Ko = 2^30 Octets
// printf("2 Go = %lu octets\n", capacite_octets); // Résultat : 2147483648
Opérateur Ternaire (Conditionnel)
L'opérateur ternaire est une forme concise d'instruction if-else, idéale pour des affectations conditionenlles simples. Sa syntaxe est condition ? expression_si_vrai : expression_si_faux;
int age = 20;
char * statut = (age >= 18) ? "Majeur" : "Mineur";
// printf("La personne est : %s\n", statut); // Affiche "La personne est : Majeur"
int valeur = (10 > 5) ? 100 : 200; // valeur sera 100
Autres Opérateurs Utiles
,(Virgule) : Évalue les expressions de gauche à droite, et la valeur de l'ensemble est celle de la dernière expression.sizeof: Retourne la taille en octets d'une variable ou d'un type.&(Adresse de) : Retourne l'adresse mémoire d'une variable.*(Déréférencement) : Accède à la valeur pointée par un pointeur..(Accès membre) : Accède à un membre d'une structure ou d'une union via une variable.->(Accès membre via pointeur) : Accède à un membre d'une structure ou d'une union via un pointeur.
int x_val = 10, y_val = 20;
int resultat_virgule = (x_val++, y_val * 2, x_val + y_val);
// x_val devient 11, y_val*2 est évalué (40), puis x_val+y_val (11+20=31).
// resultat_virgule sera 31.
struct Point { int x, y; };
struct Point p1_coords;
struct Point* ptr_p1_coords = &p1_coords;
p1_coords.x = 1; // Accès direct au membre x
ptr_p1_coords->y = 2; // Accès au membre y via un pointeur
Priorité et Associativité des Opérateurs
La priorité des opérateurs détermine l'ordre d'évaluation des opérations dans une expression (par exemple, la multiplication avant l'addition). L'associativité gère l'ordre lorsque des opérateurs de même priorité sont présents (gauche-à-droite ou droite-à-gauche).
Quelques règles générales pour la priorité, de la plus haute à la plus basse :
- Opérateurs unaires (
!,++,--,&,*,sizeof). - Opérateurs arithmétiques (multiplication/division/modulo avant addition/soustraction).
- Opérateurs de décalage (
<<,>>). - Opérateurs relationnels (
<,>,<=,>=). - Opérateurs d'égalité (
==,!=). - Opérateurs logiques (
&&,||). - Opérateur ternaire (
? :). - Opérateurs d'affectation (
=,+=, etc., associativité droite-à-gauche). - Opérateur virgule (
,, la plus basse priorité).
Bonnes pratiques :
- N'abusez pas de la mémorisation de toutes les règles de priorité.
- Utilisez des parenthèses
()pour clarifier l'ordre d'évaluation et améliorer la lisibilité du code. - Évitez les expressions excesivement complexes. Décomposez-les en plusieurs étapes si nécessaire.
Structures de Contrôle de Flux
Les structures de contrôle permettent de diriger l'exécution du programme en fonction de conditions ou de répéter des blocs de code.
Instructions de Branchement (Sélection)
1. Branchement Simple (if)
Exécute un bloc de code si une condition est vraie.
if (condition_expression) {
// Instructions à exécuter si la condition est vraie
}
2. Branchement Double (if-else)
Exécute un bloc de code si la condition est vraie, ou un autre bloc si elle est fausse.
if (condition_expression) {
// Instructions si vrai
} else {
// Instructions si faux
}
3. Brenchement Multiple (else if)
Permet de tester plusieurs conditions séquentiellement.
if (condition_1) {
// Instructions si condition_1 est vraie
} else if (condition_2) {
// Instructions si condition_2 est vraie (et condition_1 est fausse)
} else if (condition_3) {
// Instructions si condition_3 est vraie (et condition_1 et 2 sont fausses)
} else {
// Instructions si aucune des conditions précédentes n'est vraie
}
4. Branchement Multiple (switch-case)
Idéal pour choisir une exécution parmi de nombreuses options basées sur la valeur d'une expression.
char choix_utilisateur = 'b';
switch (choix_utilisateur) {
case 'a':
// printf("Option A sélectionnée.\n");
break;
case 'b':
case 'B': // Permet de regrouper des cas pour la même exécution
// printf("Option B sélectionnée.\n");
break; // Crucial pour éviter le "fall-through"
case 'c':
// printf("Option C sélectionnée.\n");
break;
default:
// printf("Choix invalide.\n");
break; // Bien que facultatif ici, c'est une bonne pratique
}
Points importants concernant switch-case :
- L'expression suivant
switchdoit être de type entier (char,short,int,long) ou énumération. - Les valeurs des
casedoivent être des constantes littérales ou des expressions constantes, pas des variables. - Le bloc
defaultest facultatif et est exécuté si aucune valeurcasene correspond. - L'instruction
breakest cruciale pour sortir du blocswitchaprès l'exécution d'uncase. Sansbreak, l'exécution "traverse" lescasesuivants (phénomène de fall-through) jusqu'à unbreakou la fin duswitch.
5. Branchements Imbriqués
Les structures de branchement peuvent être imbriquées les unes dans les autres pour gérer des logiques plus complexes.
int age_visiteur = 25;
int a_carte_fidelite = 1; // 1 pour oui, 0 pour non
if (age_visiteur >= 18) {
// printf("Accès autorisé.\n");
if (a_carte_fidelite) {
// printf("Vous bénéficiez d'une réduction.\n");
} else {
// printf("Considérez de devenir membre pour des avantages.\n");
}
} else {
// printf("Accès refusé, vous êtes trop jeune.\n");
}
Instructions de Boucle (Répétition)
1. Boucle while
Répète un bloc de code tant qu'une condition reste vraie. La condition est évaluée avant chaque itération.
int indice_w = 0;
while (indice_w < 3) {
// printf("Ceci est l'itération %d\n", indice_w);
indice_w++;
}
Une boucle while(1) crée une boucle infinie (ou "boucle morte") :
// while (1) {
// // Ce code s'exécutera indéfiniment
// }
2. Boucle do-while
Similaire à while, mais garantit que le bloc de code est exécuté au moins une fois, car la condition est évaluée après la première exécution.
int cpt_do = 0;
do {
// printf("Exécution do-while, compteur : %d\n", cpt_do);
cpt_do++;
} while (cpt_do < 2);
// Même si cpt_do était initialisé à 5, le bloc s'exécuterait une fois avant de vérifier la condition.
3. Boucle for
Conçue pour des boucles avec un nombre connu d'itérations, elle combine l'initialisation, la condition et la mise à jour du compteur en une seule ligne.
for (int f_val = 0; f_val < 4; f_val++) {
// printf("Boucle for, valeur de f_val : %d\n", f_val);
}
Les éléments de la boucle for sont exécutés dans l'ordre suivant :
- Initialisation (
int f_val = 0) : Exécutée une seule fois au début de la boucle. - Condition (
f_val < 4) : Évaluée avant chaque itération. Si vraie, le corps de la boucle est exécuté. Si fausse, la boucle se termine. - Corps de la boucle : Les instructions à l'intérieur des accolades.
- Mise à jour (
f_val++) : Exécutée après chaque itération du corps de la boucle.
Une boucle for(;;) est une autre façon de créer une boucle infinie :
// for (;;) {
// // Ce code s'exécutera indéfiniment
// }
4. Boucles Imbriquées
Il est courant d'imbriquer des boucles, par exemple pour parcourir des tableaux multidimensionnels ou générer des motifs.
for (int ligne = 0; ligne < 2; ligne++) {
for (int col = 0; col < 3; col++) {
// printf("(%d, %d) ", ligne, col);
}
// printf("\n");
}
/* Afficherait :
(0, 0) (0, 1) (0, 2)
(1, 0) (1, 1) (1, 2)
*/