59 votes

Oublié do dans la boucle do... while

J'ai eu un bug ennuyeux, où j'ai oublié d'écrire do dans un do ... while boucle.

int main() {
  int status=1;
  /*do*/ {
    status = foo();
  } while (status);
}

Pourquoi cela compile-t-il et s'exécute-t-il toujours ? Il me semble que le compilateur devrait le rejeter comme n'ayant aucun sens ou au moins émettre un avertissement (j'ai -Wall dans les options de mon compilateur). J'utilise C++11.

D'après ce que je peux dire, le code renforcé s'exécute { ... } et ensuite le programme vérifie la condition dans la clause while à l'infini.

92voto

CherryDT Points 4046

Je suppose que vous en fait avait int status à l'extérieur de du corps de la boucle, sinon le code ne serait pas compiler. (Même pas avec le site do en place).

Cela étant corrigé, le code que vous avez écrit est toujours valable sans do mais fait quelque chose de différent, comme vous l'avez déjà noté correctement. Permettez-moi de le réécrire légèrement pour montrer comment il est interprété :

int main () {
  int status;

  { // anonymous braced block that just creates a new scope
    status = foo();
  }

  while (status) {
    // empty loop body
  }
}

Un bloc autonome comme celui-là a son utilité par exemple pour utiliser RAII - il pourrait contenir une variable locale avec un objet dont le destructeur libère une ressource lorsqu'il sort de son champ d'application (par exemple un handle de fichier), entre autres choses.

La raison pour laquelle le while (status); est la même chose que while (status) {} c'est parce que vous êtes autorisé à mettre soit une seule déclaration soit un bloc, et que ; est une déclaration valide qui ne fait rien.

Et écrire quelque chose comme <code>while (someVariable);</code> n'est même pas absurde en général (bien qu'elle le soit dans ce cas) car il s'agit essentiellement d'un spinlock, une forme d'attente occupée - il quitterait la boucle si un autre cœur de processeur, un composant d'E/S ou une interruption modifiait la valeur de <code>someVariable</code> afin que la condition ne soit plus remplie, et elle le ferait sans délai. Vous n'écririez probablement pas un tel code sur une plate-forme de bureau où "monopoliser le CPU" est une mauvaise chose (sauf dans des scénarios spécifiques dans le code en mode noyau), mais sur un dispositif intégré comme un microcontrôleur (où votre code est le seul code qui s'exécute), cela peut être une façon parfaitement valide d'implémenter un code qui attend un changement externe. Comme l'a souligné Acorn dans les commentaires, cela n'aurait bien sûr de sens que si <code>someVariable</code> étaient <code>volatile</code> (ou autrement non prévisible), mais je parle des boucles occupées sur une variable en général.

14voto

Martin Konrad Points 847

Le compilateur ne peut pas soulever d'erreur ici puisque, selon la section 6.5 de la norme C++11, ce code est parfaitement valide. En fait, il existe deux types de while :

  1. while ( condition ) statement
  2. do statement while ( expression );

statement peut être

  • une seule déclaration ou
  • un bloc d'instructions entre accolades ou
  • la déclaration vide ( ; )

Avec ceci en tête, laissez-moi formater votre code de la manière dont le compilateur le voit :

int main () {
  int status;

  { // braced block that just creates a new scope
    status = foo();
  }

  while (status) /* empty statement */;
}

S'il peut être évident pour un lecteur humain que vous avez voulu boucler le code entre accolades, ce n'est pas évident pour le compilateur. Cela est dû au fait que les compilateurs C++ ne tiennent généralement pas compte de l'indentation et des sauts de ligne. Un outil d'analyse qui les prendrait en compte pourrait vous avertir que la façon dont vous avez formaté votre code ne correspond pas à ce qu'il fait réellement et le corriger pour vous. L'erreur serait ainsi plus évidente pour vous. Ou peut-être qu'un jour nous aurons une fonctionnalité du langage qui nous permettra de dire explicitement "déclaration vide". Cela nous permettrait d'exprimer clairement notre intention. Une fois que nous aurons cette fonctionnalité, les compilateurs pourront émettre un avertissement lorsque le code n'est pas clair. D'ici là, nous devons être prudents - le C++ est un langage puissant, mais il a quelques bords tranchants...

BTW vous êtes pas le premier qui ont tiré des conclusions erronées de l'indentation et des sauts de ligne.

6voto

Acorn Points 6838

Pourquoi cela compile-t-il et s'exécute-t-il toujours ?

Ce n'est pas le cas parce que status n'est pas défini.

Il me semble que le compilateur devrait rejeter cette idée comme étant absurde ou au moins afficher un avertissement (j'ai le paramètre -Wall dans mes options de compilation).

En supposant que vous définissez status il s'agit d'un programme valide. Certains compilateurs ou analyseurs peuvent générer des avertissements en cas de boucles infinies ou d'interruptions (no-op). while corps.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X