La semaine dernière, nous avons vu l'instruction if, qui permettait de tester une condition, et d'exécuter du code un fonction du résultat du test. Cette instruction est très puissante et très utile, mais elle a ses limites. Imaginez un instant qu'on ait une variable contenant un code, et qu'à chaque code est associé une action à faire. Si il n'y a que trois codes différents, on peut faire trois ifs, du genre :
int code = (quelque chose);
if(code == 0) cout << "Mise en route de la machine!\n"; else if(code == 1) cout << "Mise en route des auxiliaires!\n"; else if(code == 2) cout << "Fin du processus!\n"; else cout << "Code inconnu!!!\n"; |
Mais cette écriture est lourde, et n'explique pas vraiment ce que fait le programme : on comprend, mais ce genre de tests n'est pas du domaine du if. Il existe pour cela une autre instruction, switch, qui compare une variable à un certain nombre de valeurs contantes, et exécute du code en fonction de cette variable.
Voici, avec l'exemple ci-dessus, l'équivalent switch :
int code = (quelque chose);
switch(code) case 0: cout << "Mise en route de la machine!\n"; case 1: cout << "Mise en route des auxiliaires!\n"; case 2: cout << "Fin du processus!\n"; default: cout << "Code inconnu!!!\n"; } |
Mais avant de vous expliquer ce petit bout de code plus en détails, je vais aborder les constantes, qu'on utilise largement en programmation, et qui trouvent très bien leur place avec l'instruction switch.
Les constantes
En programmation, on utilise souvent des constantes. Les constantes sont des variables, avec leur type, mais qu'on ne peut plus modifier après leur création. Un exemple de constante, c'est la valeur pi. Si vous #includez le fichier math.h, vous y trouverez définie une constante de type float qui s'appelle M_PI, et dont la valeur est 3.14159. Pourquoi en faire une constante? Imaginez que quelque part dans votre programme vous modifiez cette variable! Que se passe-t-il alors pour le reste du programme qui utilise cette valeur? Si vous tentez de modifier une constante, le compilateur refusera purement et simplement cette tentative sabotage, et protège ainsi votre programme.
Un seconde raison est que, comme en mathématiques, il est plus facile d'écrire M_PI que d'écrire 3.14159 partout dans le programme. C'est plus facile à lire, et le nom permet de savoir ce que représente cette valeur. De plus, vous fixez la valeur de votre constante une seule fois, ce qui fait que si vous voulez modifier cette valeur pour recompiler, vous n'avez qu'une ligne de code à modifier, et vous n'êtes pas obligés de parcourir tout votre programme pour changer sa valeur à chaque fois qu'elle apparaît. C'est tout de même plus pratique.
Encore une raison d'utiliser des constantes pour des variables dont la valeur ne change pas est que le compilateur peut optimiser l'utilisation de cette valeur : il peut la remplacer directement par sa valeur dans le code, ce qui est plus rapide que de prendre la valeur d'une variable. Mais ça, c'est purement technique.
J'espère donc vous avoir convaicu de l'utilité des constantes dans les programmes. Voici maintenant comment créer une constante. Cela se fait exactement comme une création de variable normale, mais vous rajoutez le mot clé const devant :
const float M_PI = 3.14159;
|
Vous indiquez ainsi que vous n'avez plus l'intention de changer la valeur de la variable, autrement dit que toute affectation passée cette ligne se soldera par une erreur de compilation. Il faut donc donner à la constante sa valeur dès la déclaration. Vous ne pouvez pas faire comme suit :
const float M_PI;
M_PI = 3.14159; |
Le compilateur (DJGPP pour l'exemple) vous sortirait deux erreurs :
Ensuite, l'utilisation d'une constante se fait comme avec toute autre variable, mais sans pouvoir changer sa valeur.
Utiliser des constantes permet, comme je vous l'ai dit, d'utiliser une valeur sans réellement la connaitre. Ainsi, si d'un système à l'autre la même couleur est codée par deux valeurs différentes, le nom de la constante ne change pas, et vous pourrez recompiler votre programme sur les deux systèmes, sans avoir à changer une seule ligne de code (les couleurs sont en général définies dans les fichiers en-tête .h). Comm les constantes ont des noms, on peut plus facilement voir à quoi elles correspondent. Ainsi, définissons quelques constantes pour notre exemple de départ, et réécrivons le programme :
const int CODE_START_MACHINE = 0;
const int CODE_START_SECONDARY = 1; const int CODE_END_PROCESS = 2; int code = (quelque chose); switch(code) case CODE_START_MACHINE: cout << "Mise en route de la machine!\n"; case CODE_START_SECONDARY: cout << "Mise en route des auxiliaires!\n"; case CODE_END_PROCESS: cout << "Fin du processus!\n"; default: cout << "Code inconnu!!!\n"; } |
Avouez que c'est plus explicite. Par convention, on donne aux constantes des noms en majuscules, dont les mots sont séparés avec le caractère "_", et on met un préfixe indiquant le contexte (ici, CODE_).
#define
Si vous avez déjà regardé un fichier source en C, vous avez sans doute croisé des instructions du genre :
#define MAX 1000
Vous avez reconnu, sans doute, une directive s'adressant au préprocesseur. C'est là la façon dont on définit une constante en C. Cette expression signifie : dans le code source, changer toutes les occurences de MAX en 1000 avant de compiler. C'est ainsi que si vous écrivez :
int x = MAX;
le compilateur, lui, verrait :
int x = 1000;
Le problème de ce genre de substitution, c'est que le compilateur ne voit jamais le MAX, et ne peut pas vous donner d'information précise en cas d'erreur de compilation. Il ne peut pas non plus faire une vérification de types. Les constantes, telles qu'elles sont définies en C++, elles, permettent toutes ces vérifications, et il est donc préférable de les utiliser.
ais maintenant que je vous ai expliqué les constantes, revenons à l'instruction switch.
Switch
Dans notre exemple, l'instruction switch compare la valeur de code aux valeurs précisées par case valeur:. Si la valeur correspond, il exécute tout le code qui suit le case correspondant, jusqu'à la fin du switch, ou jusqu'à ce qu'il trouve l'instruction break. Cette instruction termine les tests de comparaison et continue le programme à l'instruction qui suit le bloc switch. Un case ne s'arrête donc pas automatiquement au case qui suite, ce qui peut paraître un peu déroutant pour quelqu'un qui aurait déjà rencontré ce genre d'instruction dans d'autres langages. Ceci à quelques avantages :
const int CODE_START_MACHINE = 0;
const int CODE_START_SECONDARY = 1; const int CODE_END_PROCESS = 2; const int CODE_EMERGENCY_EXIT = 3; int code = (quelque chose); switch(code) case CODE_START_MACHINE: cout << "Mise en route de la machine!\n"; case CODE_START_SECONDARY: cout << "Mise en route des auxiliaires!\n"; case CODE_EMERGENCY_EXIT: cout << "Arrêt d'urgence activé, interruption immédiate!!!\n"; case CODE_END_PROCESS: cout << "Fin du processus!\n"; default: cout << "Code inconnu!!!\n"; } |
Pour les 3 valeurs d'origine, le programme se comporte comme précédemment : il affiche le message correspondant à la valeur de code, et puis c'est fini. Par contre, il y a un nouveau code : CODE_EMERGENCY_EXIT, un code signalant un arrêt d'urgence (en cas de danger par exemple). Voici ce qui s'affiche lorsque code vaut CODE_EMERGENCY_EXIT :
Arrêt d'urgence activé, interruption immédiate!!!
Fin du processus! |
En effet, le case CODE_EMERGENCY_EXIT ne contient pas d'instruction break, donc l'exécution continue son cours, et arrive au case suivant, celui du CODE_END_PROCESS. Ceci est important : c'est ainsi, par exemple, que vous pouvez donner à plusieurs constantes la même signification. Il suffit pour cela de mettre tous les case CONSTANTE: les uns à la suite des autres et de mettre le code à exécuter après le dernier de la série.
Il reste une dernière instruction à voir : le cas default:. C'est tout simplement le cas qui est exécuté si aucune valeur de la liste ne correspond à la valeur de la variable testée. Il n'est pas nécessaire d'avoir un default dans un bloc switch, mais il permet de prendre une mesure si aucune valeur ne correspond. On peut ainsi imaginer qu'une mauvaise valeur de code mène à la conclusion que quelqu'un à touché au système, provoquant un message d'alerte. C'est une des possibilités. Si il n'y a pas de default, il ne se passe rien, et l'exécution continue après le bloc switch.
Si vous mettez des instructions avant le premier case ou entre un break et le case suivant, ce code ne sera jamais exécuté, car il ne correspond à aucun cas. Si vous faites cela, le compilateur vous sortira une mise en garde (exemple de djgpp):
warning: unreachable code at beginning of switch statement
Autrement dit, vous avez écrit du code inutile, car inaccessible. Les mises en garde de ce type n'empêchent pas la compilation, mais elles vous indiquent que votre programme comporte une erreur potentielle. Un programme bien écrit ne doit générer aucune mise en garde.
Il reste un dernier point très important à savoir :
![]() |
|
Conclusion
Voilà donc pour les constantes et le switch. Une des applications du switch est par exemple de tester les messages qui sont envoyés par Windows ou par une autre application à une fenêtre, pour savoir ce qui se passe et pour réagir aux actions de l'utilisateur. C'est d'ailleurs le même principe pour toutes les interfaces graphiques, telles que BeOS ou XWindow pour Linux/Unix. Pour les constantes, c'est très simple : il y en a partout, et il faut les connaître. De nombreuses constantes sont définies dans les fichiers en-tête .h, ce qui est très utile quand on sait qu'elles sont là (il faut donc avoir une bonne documentation avec son compilateur).
Le programme du jour est le même que le dernier exemple que nous avons vu, c'est-à-dire qu'il faut le compléter afin qu'il soit compilable. Voici ce qu'il faut retenir :
![]() |
|
Et, selon la tradition, les petits exercices, peu nombreux cette fois-ci :
![]() |
|
Voilà. Ceci ne devrait pas vous poser plus de problèmes que ça. La semaine prochaine, nous verrons les boucles. C'est un morceau très important, et presque tous les programmes un peu plus évolués que "Hello, World!" en ont.
Voir aussi: Les structures de sélection : première approche