Tests et conditions

Jusqu’à présent, vous avez appris à écrire du texte, manipuler des nombres et interagir un tout petit peu avec l’utilisateur.

En gros, pour le moment, un programme est quelque chose de sacrément simple et linéaire, ce dernier ne nous permettant que d’exécuter des instructions dans un ordre donné. Techniquement, une simple calculatrice peut en faire autant (voire plus). Cependant et heureusement, les langages de programmation actuels fournissent des moyens permettant de réaliser des tâches plus évoluées.

Pour ce faire, diverses structures de contrôle ont été inventées. Celles-ci permettent de modifier le comportement d’un programme suivant la réalisation de différentes conditions. Ainsi, si une condition est vraie, le programme se comportera d’une telle façon et à l’inverse, si elle est fausse, le programme fera telle ou telle chose.

Dans ce chapitre, nous allons voir comment rédiger des conditions à l’aide de deux catégories d’opérateurs :

  • les opérateurs de comparaison, qui comparent deux nombres ;
  • les opérateurs logiques, qui permettent de combiner plusieurs conditions.

Les booléens

Comme les opérateurs que nous avons vus précédemment (+, -, *, etc), les opérateurs de comparaison et les opérateurs logiques donnent un résultat : « vrai » si la condition est vérifiée, et « faux » si la condition est fausse. Toutefois, comme vous le savez, notre ordinateur ne voit que des nombres. Aussi, il est nécessaire de représenter ces valeurs à l’aide de ceux-ci.

Certains langages fournissent pour cela un type distinct pour stocker le résultat des opérations de comparaison et deux valeurs spécifiques : true (vrai) et false (faux). Néanmoins, dans les premières versions du langage C, ce type spécial n’existait pas (le type _Bool a été introduit par la norme C99). Il a donc fallu ruser et trouver une solution pour représenter les valeurs « vrai » et « faux ». Pour cela, la méthode la plus simple a été privilégiée : utiliser directement des nombres pour représenter ces deux valeurs. Ainsi, le langage C impose que :

  • la valeur « faux » soit représentée par zéro ;
  • et que la valeur « vrai » soit représentée par tout sauf zéro.

Les opérateurs de comparaison et les opérateurs logiques suivent cette convention pour représenter leur résultat. Dès lors, une condition vaudra 0 si elle est fausse et 1 si elle est vraie.

Notez qu’il existe un en-tête <stdbool.h> qui fournit deux constantes entières : true (qui vaut 1) et false, qui vaut 0 ainsi qu’un synonyme pour le type _Bool nommé bool.

#include <stdbool.h>
#include <stdio.h>


int
main(void)
{
    bool booleen = true;

    printf("true = %u, false = %u\n", true, false);
    printf("booleen = %u\n", booleen);
    return 0;
}
Résultat
true = 1, false = 0
booleen = 1

Les opérateurs de comparaison

Le langage C fournit différents opérateurs qui permettent d’effectuer des comparaisons entre des nombres. Ces opérateurs peuvent s’appliquer aussi bien à des constantes qu’à des variables (ou un mélange des deux). Ces derniers permettent donc par exemple de vérifier si une variable est supérieure à une autre, si elles sont égales, etc.

Comparaisons

L’écriture de conditions est similaire aux écritures mathématiques que vous voyez en cours : l’opérateur est entre les deux expressions à comparer. Par exemple, dans le cas de l’opérateur > (« strictement supérieur à »), il est possible d’écrire des expressions du type a > b, qui vérifie si la variable a est strictement supérieure à la variable b.

Le tableau ci-dessous reprend les différents opérateurs de comparaison.

Opérateur Signification
== Égalité
!= Inégalité
< Strictement inférieur à
<= Inférieur ou égal à
> Strictement supérieur à
>= Supérieur ou égal à

Ces opérateurs ne semblent pas très folichons. En effet, avec, nous ne pouvons faire que quelques tests basiques sur des nombres. Cependant, rappelez-vous : pour un ordinateur, tout n’est que nombre et comme pour le stockage des données (revoyez le début du chapitre sur les variables si cela ne vous dit rien) il est possible de ruser et d’exprimer toutes les conditions possibles avec ces opérateurs (cela vous paraîtra plus clair quand nous passerons aux exercices).

Résultat d’une comparaison

Comme dit dans l’extrait plus haut, une opération de comparaison va donner zéro si elle est fausse et un si elle est vraie. Pour illustrer ceci, vérifions quels sont les résultats donnés par différentes comparaisons.

#include <stdio.h>


int main(void)
{
    printf("10 == 20 vaut %d\n", 10 == 20);
    printf("10 != 20 vaut %d\n", 10 != 20);
    printf("10 < 20 vaut %d\n", 10 < 20);
    printf("10 > 20 vaut %d\n", 10 > 20);
    return 0;
}
Résultat
10 == 20 vaut 0
10 != 20 vaut 1
10 < 20 vaut 1
10 > 20 vaut 0

Le résultat confirme bien ce que nous avons dit ci-dessus.

Les opérateurs logiques

Toutes ces comparaisons sont toutefois un peu faibles seules car il y a des choses qui ne sont pas possibles à vérifier en utilisant une seule comparaison. Par exemple, si un nombre est situé entre zéro et mille (inclus). Pour ce faire, il serait nécessaire de vérifier que celui-ci est supérieur ou égal à zéro et inférieur ou égal à mille.

Il nous faudrait donc trouver un moyen de combiner plusieurs comparaisons entre elles pour résoudre ce problème. bien rassurez-vous, le langage C fournit de quoi associer plusieurs résultats de comparaisons : les opérateurs logiques.

Les opérateurs logiques de base

Il existe trois opérateurs logiques. L’opérateur « et », l’opérateur « ou », et l’opérateur de négation. Les deux premiers permettent de combiner deux conditions alors que le troisième permet d’inverser le sens d’une condition.

L’opérateur « et »

L’opérateur « et » va manipuler deux conditions. Il va donner un si elles sont vraies, et zéro sinon.

Première condition Seconde condition Résultat de l’opérateur « et »
Fausse Fausse 0
Fausse Vraie 0
Vraie Fausse 0
Vraie Vraie 1

Cet opérateur s’écrit && et s’intercale entre les deux conditions à combiner. Si nous reprenons l’exemple vu plus haut, pour combiner les comparaisons a >= 0 et a <= 1000, nous devons placer l’opérateur && entre les deux, ce qui donne l’expression a >= 0 && a <= 1000.

L’opérateur « ou »

L’opérateur « ou » fonctionne exactement comme l’opérateur « et », il prend deux conditions et les combine pour former un résultat. Cependant, l’opérateur « ou » vérifie que l’une des deux conditions (ou que les deux) est (sont) vraie(s).

Première condition Seconde condition Résultat de l’opérateur « ou »
Fausse Fausse 0
Fausse Vraie 1
Vraie Fausse 1
Vraie Vraie 1

Cet opérateur s’écrit || et s’intercale entre les deux conditions à combiner. L’exemple suivant permet de déterminer si un nombre est divisible par trois ou par cinq (ou les deux) : (a % 3 == 0) || (a % 5 == 0). Notez que les parenthèses ont été placées par souci de lisibilité.

L’opérateur de négation

Cet opérateur est un peu spécial : il manipule une seule condition et en inverse le sens.

Condition Résultat de l’opérateur de négation
Fausse 1
Vraie 0

Cet opérateur se note !. Son utilité ? Simplifier certaines expressions. Par exemple, si nous voulons vérifier cette fois qu’un nombre n’est pas situé entre zéro et mille, nous pouvons utiliser la condition a >= 0 && a <= 1000 et lui appliquer l’opérateur de négation, ce qui donne !(a >= 0 && a <= 1000).

Faites bien attention à l’utilisation des parenthèses ! L’opérateur de négation s’applique à l’opérande le plus proche (sans parenthèses, il s’agirait de a). Veillez donc a bien entourer de parenthèses l’expression concernée par la négation.

Notez que pour cet exemple, il est parfaitement possible de se passer de cet opérateur à l’aide de l’expression a < 0 || a > 1000. Il est d’ailleurs souvent possible d’exprimer une condition de différentes manières.

Évaluation en court-circuit

Les opérateurs && et || evaluent toujours la première condition avant la seconde. Cela paraît évident, mais ce n’est pas le cas dans tous les langages de programmation. Ce genre de détail permet à ces opérateurs de disposer d’un comportement assez intéressant : l’évaluation en court-circuit.

De quoi s’agit-il ? Pour illustrer cette notion, reprenons l’exemple précédent : nous voulons vérifier si un nombre est compris entre zéro et mille, ce qui donne l’expression a >= 0 && a <= 1000. Si jamais a est inférieur à zéro, nous savons dès la vérification de la première condition qu’il n’est pas situé dans l’intervalle voulu. Il n’est donc pas nécessaire de vérifier la seconde. bien, c’est exactement ce qu’il se passe en langage C. Si le résultat de la première condition suffit pour déterminer le résultat de l’opérateur && ou ||, alors la seconde condition n’est pas évaluée. Voilà pourquoi l’on parle d’évaluation en court-circuit.

Plus précisément, ce sera le cas pour l’opérateur && si la première condition est fausse et pour l’opérateur || si la première condition est vraie (relisez les tableaux précédents si cela ne vous semble pas évident).

Ce genre de propriété peut-être utilisé efficacement pour éviter de faire certains calculs, en choisissant intelligemment quelle sera la première condition.

Combinaisons

Bien sûr, il est possible de mélanger ces opérateurs pour créer des conditions plus complexes. Voici un exemple un peu plus long (et inutile, soit dit en passant :-° ).

int a = 3;
double b = 64.67;
double c = 12.89;
int d = 8;
int e = -5;
int f = 42;
int r;

r = ((a < b && b > 32) || (c < d + b || e == 0)) && (f > d);
printf("La valeur logique est égale à : %d\n", r);

Ici, la variable r est égale à 1, la condition est donc vraie.

Priorité des opérations

Comme pour les opérateurs mathématiques, les opérateurs logiques et de comparaisons ont des règles de priorité (revoyez le chapitre sur les opérations mathématiques si cela ne vous dit rien). En regardant le code écrit plus haut, vous avez d’ailleurs sûrement remarqué la présence de plusieurs parenthèses. Celles-ci permettent d’enlever toute ambigüité dans les expressions créées avec des opérateurs logiques.

Les règles de priorité sont les suivantes, de la plus forte à la plus faible :

  1. l’opérateur de négation (!) ;
  2. les opérateurs relationnels (<, <=, >, >=) ;
  3. les opérateurs d’égalité et d’inégalité (==, !=) ;
  4. l’opérateur logique && ;
  5. l’opérateur logique ||.

Ainsi, les expressions suivantes seront évaluées comme suit.

  • 10 < 20 == 0 : 10 < 20, puis l’égalité ;
  • !a != 1 < 0 : !a, puis 1 < 0, puis l’inégalité ;
  • a && b || c && d, a && b, puis c && d, puis le ||.

Si vous souhaitez un résultat différent, il est nécessaire d’ajouter des parenthèses pour modifier les règles de priorité, par exemple comme suit a && (b || c) && d auquel cas l’expression b || c est evaluée la première, suivie du premier &&, puis du second.


Au prochain chapitre, nous allons combiner les conditions avec une seconde notion : les sélections.

En résumé
  1. Une condition est soit vrai, soit fausse.
  2. Une condition fausse est représentée par une valeur nulle, une condition vraie par une valeur non nulle ;
  3. Les opérateurs && et || utilisent l’évaluation en court-circuit : si la première condition est suffisante pour déterminer le résultat, la seconde n’est pas évaluée.
  4. Les opérateurs de comparaisons et les opérateurs logiques ont également des règles de priorité.