Boucle infinie en C : for ou while ?
On s’est déjà posé cette question, on a déjà vu ou entendu des gens la poser : faut-il faire une boucle infinie avec while (1)
ou avec for( ; ; )
en C ?
La logique veut que le résultat soit le même. J’ai donc fait le test avec le compilateur MinGW sous Windows 7 64 bits. J’ai pour cela écrit deux fonctions placées dans le fichier boucles.c
:
void withFor(void) { for(;;) ; } void withWhile(void) { while(1) ; }
J’ai ensuite utilisé objdump
pour déassembler le code :
PS D:\Users\pgradot\Documents\C\out\Debug> objdump.exe -d .\boucles.o .\boucles.o: file format pe-i386 Disassembly of section .text: 00000000 : 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: eb fe jmp 3 00000005 : 5: 55 push %ebp 6: 89 e5 mov %esp,%ebp 8: eb fe jmp 8 a: 90 nop b: 90 nop
Les 2 NOP à la fin m’ont étonné. J’ai inversé les positions des fonctions dans le fichier et j’ai alors constaté que les 2 NOP étaient toujours à la fin du fichier déassemblé. Pour m’assurer qu’ils ne faisaient effectivement pas partie des fonctions (vu les JMP, c’était quasiment certain), j’ai rajouté une fonction à la fin du fichier :
void withWhile(void) { while(1) ; } void withFor(void) { for(;;) ; } void nop() { }
Voici le résultat en assembleur :
PS D:\Users\pgradot\Documents\C\out\Debug> objdump.exe -d .\boucles.o .\boucles.o: file format pe-i386 Disassembly of section .text: 00000000 : 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: eb fe jmp 3 00000005 : 5: 55 push %ebp 6: 89 e5 mov %esp,%ebp 8: eb fe jmp 8 0000000a : a: 55 push %ebp b: 89 e5 mov %esp,%ebp d: 5d pop %ebp e: c3 ret f: 90 nop
Il n’y a donc aucune différence de performance entre les deux boucles, en tout cas avec ce compilateur et cette cible. J’ai des résultats similaires avec llvm/gcc
et otool
sous Mac OS X. Il en y a qui disent que for( ; ; )
donnerait de meilleures performances que while (1)
car il y a une évaluation de condition dans la deuxième écriture. A l’évidence, mon compilateur ne se laisse pas avoir. Ce n’était peut-être pas le cas avec des compilateurs anciens mais je pense qu’on peut dire que cela relève maintenant des mythes du passé.
Il peut enfin rester des considérations sémantiques : laquelle des deux écritures représentent mieux l’idée de boucle infinie ? Je pense que cette discussion de stackoverflow les résume bien. En particulier, j’aime beaucoup l’écriture suivante qui y est proposée et donc la sémantique est parfaite ^^ :
#define EVER ;; void forever(void) { for(EVER) ; }
On peut regarder le code en sortie du pré-processeur :
gcc -E -P boucles.c void forever(void) { for(;;) ; }
Et enfin l’assembleur généré :
otool -tv boucles.o boucles.o: (__TEXT,__text) section _forever: 0000000000000000 pushq %rbp 0000000000000001 movq %rsp,%rbp 0000000000000004 jmp 0x00000004
Avis personnel : je met toujours while (1) (ou while (true) en Java). C’est pour moi l’écriture la plus claire à lire et surtout la plus facile à dire.
« Hey machin, j’ai fait une boucle for(;;) !
– Gné ?!
– Oui, un while un quoi !
– Ahhhhh ! »