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 d’équivalent naturel dans la langue française! Au mieux on peut utiliser une périphrase comme : stocker dans estPositif la valeur de vérité de l’expression «x est plus grand que zéro».

À retenir :

  • Chaque fois que 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 ont 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ément :

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éenne.

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;

  • Le bloc d’instructions d’un if peut ê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écalons chaque ligne de quatre espaces par 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 cela est possible, utiliser une expression booléenne plutôt qu’une instruction conditionnelle;

  • Erreur classique : insérer accidentellement une instruction vide ; ;

  • Erreur classique : imbriquer incorrectement if et else.

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