Cours: conditionnelles: erreurs classiques

Nous allons maintenant voir quelques bonnes pratiques et erreurs classiques lorsque l’on utilise des instructions conditionnelles.

Exemple 1

Dans cet exemple, on souhaite affecter à la variable estPositif la valeur «Vrai» si \(x \geq 0\) et «Faux» sinon. Cela se traduit litéralement par:

int x = 4;
bool estPositif;

if ( x >= 0 ) {
    estPositif = true;
} else {
    estPositif = false;
}

estPositif

Mais ne pourrait-on pas faire mieux?

https://upload.wikimedia.org/wikipedia/commons/c/ce/George_Boole_color.jpg

Si!

Grâce à George Boole qui a réalisé que «Vrai» et «Faux» sont des valeurs comme les autres avec lesquelles on peut calculer (expressions booléennes) et que l’on peut stocker (variables booléennes).

Exécutez les cellules ci-dessus et ci-dessous pour différentes valeurs de x et vérifier que le résultat est le même:

x = 4
x >= 0
bool estPositif =  x >= 0;

estPositif

C’est plus concis. Plus efficace. Avec moins de risque d’erreur.

Pourquoi cela nous paraît-il moins naturel au premier abord? Parce qu’il n’y a pas de bon équivalent dans la langue française! Au mieux on peut utiliser une périphrase comme: mettre dans estPositif la valeur de vérité de l’expression «x est plus grand que zéro».

À retenir:

  • Lorsque c’est possible, utiliser une expression booléenne plutôt qu’une instruction conditionnelle.

Exemple 2

Devinez le contenu de la variable y à la fin de l’exécution du programme suivant. Vérifiez en l’exécutant:

int x = 0;
int y = 0;

if ( x = 1 ) {
    y = 4;
}

y

Pourtant x n’est pas égal à 1! L’instruction y=4 n’aurait pas dû être exécutée!?!

Sauf que … Nous avons utilisé une affectation = et non un test d’égalité ==! Cela se voit à la valeur de x:

x

Jupyter+cling nous a prévenu avec une alerte (warning): ce n’est probablement pas ce que l’on souhaitait faire.

Il se trouve cependant que le code ci-dessus est valide en C++, ce qui est une cause classique de bogue.

**Pour les curieux: ♣

En toute logique le code ci-dessus devrait être invalide et déclencher une erreur. En effet, une affectation, c’est juste une instruction qui décrit une action à effectuer. Une action, cela n’a pas de valeur. Cependant de nombreux languages, dont C++, choisissent de faire de l’affectation une expression, en décidant de lui attribuer une valeur: celle qui a été affectée.

Cela permet par exemple d’affecter une valeur à deux variables simultanéments:

x = y = 17

Analysez pourquoi cela marche, en l’interprétant sous la forme: x = ( y = 17 ).

Maintenant nous savons que x = 1 est une expression qui vaut 1 tout le temps; elle est en particulier de type int. Là encore le code devrait être invalide puisque l’on a dit que if prenait comme condition une expression booléene.

Sauf que … C++ hérite de C une vieille convention qui date de l’époque où les languages n’avaient pas de type bool. On représentait alors les valeurs booléennes par des entiers, en représentant «Faux» par 0 et «Vrai» par n’importe quel nombre entier non nul. De ce fait l’expression x = 1 a pour valeur 1 qui est interprétée comme «Vrai» dans le contexte d’une condition.

Exercice: ♣

Quelles sont les valeurs de x et y à la fin du programme suivant:

int x = 1;
int y = 2;
if ( x = 0 ) {
    y = 3;
}

Erreurs classiques avec les conditionnelles

Devinez ce qu’affiche le programme suivant? Vérifiez en l’exécutant:

int x = 0;
int y = 0;

if ( x == 1 ); {
    y = 4;
}

y

Ce programme est équivalent à:

      if ( x == 1 );
      y = 4;

qui ne tient pas compte du if et affecte toujours 4 à y (quel que soit x).

À retenir:

  • Le point-virgule ; est un séparateur d’instruction;

  • Les blocs d’instruction du if peuvent être vide en C++;

  • Il ne faut jamais de point-virgule avant un bloc d’instructions!

Tests imbriqués

Devinez ce qu’affiche le programme suivant? Vérifiez en l’exécutant:

int x = 5;
int y = 4;
std::string resultat = "";
    
if ( x >= y ) {
    if ( x == y ) {
        resultat = "égalité";
    }
else {
    resultat = "x est plus petit que y";
}
}

resultat

Oups. Il y a un bogue

Explication: en C++, la structuration est entièrement déterminée par les accolades: {, }. Les espaces et sauts de lignes ne jouent aucun rôle. Aussi, ici, le else se rapporte au deuxième if, contrairement à ce que la disposition visuelle du code pourrait faire croire.

Maintenant, Indentons correctement le code: c’est-à-dire décallons chaque ligne par quatre espaces pour chaque niveau d’imbrication dans des accolades:

resultat = "";

if ( x >= y ) {
    if ( x == y ) {
        resultat = "égalité";
    }
    else {
        resultat = "x est plus petit que y";
    }
}

resultat

La source du bogue est devenue claire, et on peut facilement le corriger:

resultat = "";

if ( x >= y ) {
    if ( x == y ) {
        resultat = "égalité";
    }
} else {
    resultat = "x est plus petit que y";
}

resultat

À retenir:

  • Un else se rapporte au dernier if rencontré;

  • En C++, la structuration est déterminée pas les accolades;

  • La mauvaise indentation induit en erreur le lecteur!

Apparté: l’indentation

  • L”indentation consiste à espacer les lignes de code par rapport au bord gauche de la fenêtre de saisie de texte;

  • L’espacement doit être proportionnel au niveau d’imbrication des instructions du programme;

  • Quatre espaces par niveau d’imbrication est un bon compromis.

La plupart des éditeurs de texte offrent des facilités pour réaliser une bonne indentation. Apprenez les.

De la lisibilité des programmes

«Programs must be written for people to read, and only incidentally for machines to execute.»

– Harold Abelson, Structure and Interpretation of Computer Programs 1984

  • Un programme s’adresse à un lecteur

  • La lisibilité est un objectif essentiel

Conclusion

Nous avons vu les bonnes pratiques et erreurs classiques suivantes sur les instructions conditionnelles:

  • Bonne pratique: Lorsque c’est possible, utiliser une expressions booléenne plutôt qu’une instruction conditionnelle;

  • Erreur classique: Insérer accidentiellement une instruction vide ; ;

  • Erreur classique: Imbriquer incorrectement if et else;

Nous avons aussi vu l’importance de l”indentation pour la lisibilité des programmes.