Je me souviens il y a quelques années avoir eu une conversation avec un "informaticien" qui faisait dans le hype (le java à l'époque) et qui me certifiait que le C, c'est has been, et personne n'en fait "dans la vraie vie" (ie. en entreprise). Malheureusement, à l'époque j'étais déjà fondu de C. Nan, pas la poitrine de cette fille (quoi que...), et aujourd'hui encore, je persiste à croire en ce langage qui pour moi a fait ses preuves.
Cependant, depuis que j'ai terminé mes études, j'ai de moins en moins de temps à y consacrer.
Mais dernièrement, j'ai eu l'opportunité de refaire du C, et ce pour l'entreprise pour laquelle je travaille. Bref, le pur bonheur de pouvoir concilier "passion" et vie professionnelle.
Mais ce dont je voulais vraiment vous parler aujourd'hui, c'est du mot clé break en C.
Le break est donc une instruction de saut, permettant de sortir d'un bloc :
ziirish@pluto:/tmp/lab$ cat >>test1.c<<EOF
> #include <stdio.h>
> #include <stdlib.h>
>
> int
> main (int argc, char **argv)
> {
> int i;
> for (i = 0; i < 10; i++)
> {
> printf ("i: %d\n", i);
> break;
> }
>
> return 0;
> }
> EOF
ziirish@pluto:/tmp/lab$ gcc -o test1 test1.c
ziirish@pluto:/tmp/lab$ ./test1
i: 0
ziirish@pluto:/tmp/lab$
Cela agit donc comme un goto mais pour les blocs :
ziirish@pluto:/tmp/lab$ cat >>test2.c<<EOF
> #include <stdio.h>
> #include <stdlib.h>
>
> int
> main (int argc, char **argv)
> {
> int i;
> for (i = 0; i < 10; i++)
> {
> printf ("i: %d\n", i);
> goto end;
> }
>
> end:
> return 0;
> }
> EOF
ziirish@pluto:/tmp/lab$ gcc -o test2 test2.c
ziirish@pluto:/tmp/lab$ ./test2
i: 0
ziirish@pluto:/tmp/lab$
Comme pour l'argumentation concernant les goto, une utilisation abusive peut conduire à un code spaghetti et peut tout à fait être remplacer par des instructions conditionnelles. Il faut donc trouver le juste milieu entre lisibilité et maintenabilité.
L'utilisation la plus courante de break, c'est en tandem avec la structure de contrôle switch.
ziirish@pluto:/tmp/lab$ cat >>test3.c<<EOF
> #include <stdio.h>
> #include <stdlib.h>
>
> int
> main (int argc, char **argv)
> {
> if (argc < 2) return 1;
>
> char *tmp = argv[1];
> switch (tmp[0])
> {
> case '1':
> case '3':
> case '5':
> case '7':
> case '9':
> printf ("'%c' est impaire\n", tmp[0]);
> break;
> case '2':
> case '4':
> case '6':
> case '8':
> case '0':
> printf ("'%c' est paire\n", tmp[0]);
> break;
> }
>
> return 0;
> }
> EOF
ziirish@pluto:/tmp/lab$ gcc -o test3 test3.c
ziirish@pluto:/tmp/lab$ ./test3
ziirish@pluto:/tmp/lab$ echo $?
1
ziirish@pluto:/tmp/lab$ ./test3 3
'3' est impaire
ziirish@pluto:/tmp/lab$ ./test3 0
'0' est paire
ziirish@pluto:/tmp/lab$
Comme on peut le voir dans l'exemple ci-dessus, lorsque notre expression match un case, on entre dans le premier bloc disponible. Ainsi, dans le case '3':, il n'y a pas de code, mais le résultat affiché est bien celui attendu. C'est une fonctionnalité très intéressante de la fonction switch, mais qui peut vite amener à des résultats non attendus. C'est ce qui m'est arrivé.
Reprenons l'exemple précédent, en omettant le break :
ziirish@pluto:/tmp/lab$ cat >>test4.c<<EOF
> #include <stdio.h>
> #include <stdlib.h>
>
> int
> main (int argc, char **argv)
> {
> if (argc < 2) return 1;
>
> char *tmp = argv[1];
> switch (tmp[0])
> {
> case '1':
> case '3':
> case '5':
> case '7':
> case '9':
> printf ("'%c' est impaire\n", tmp[0]);
> case '2':
> case '4':
> case '6':
> case '8':
> case '0':
> printf ("'%c' est paire\n", tmp[0]);
> }
>
> return 0;
> }
> EOF
ziirish@pluto:/tmp/lab$ gcc -o test4 test4.c
ziirish@pluto:/tmp/lab$ ./test4 4
'4' est paire
ziirish@pluto:/tmp/lab$ ./test4 3
'3' est impaire
'3' est paire
ziirish@pluto:/tmp/lab$
Parfois, le bug est quand même moins facile à repérer, néanmoins, je ne troquerais pas de si tôt mon switch/case contre une armée de if