UKOnline

Opérateur

On sait maintenant qu'il existe différents types de données et que pour pouvoir utiliser des données dans un programme, on les stocke dans des variables. De plus, on a vu qu'une fois initialisée, on pouvait changer la valeur des variables. Évidemment, ce qu'on voudrait aussi pouvoir faire, c'est effectuer des calculs avec des données. C'est précisément ce qu'on va voir dans cette section, en découvrant les opérateurs grâce auxquels on peut faire des opérations sur des données. On va voir trois types différents d'opérateurs pour commencer.

Opérateur arithmétique

Les opérateurs arithmétiques permettent de faire des calculs sur des nombres entiers et flottants. Il en existe six différents en C. Le premier est l'opérateur de changement de signe qui permet de changer le signe d'une valeur. Il s'agit d'un opérateur unaire, à savoir qu'il agit sur une seule valeur. Cet opérateur se note simplement avec le signe -. Voici un exemple d'utilisation de cet opérateur :

Vous remarquerez qu'il est possible de directement insérer le résultat d'une opération dans la fonction printf. En fait, -x est une expression qui représente le calcul : « prendre l'opposé de la valeur de x ». L'exécution de cet exemple affiche à l'écran :

L'opposé de x est -12
L'opposé de y est 3.500000

On peut également directement utiliser une expression lorsqu'on modifie la valeur d'une variable. Par exemple, on pourrait écrire les lignes de code suivantes pour stocker dans la variable y l'opposé de la valeur de la variable x.

Les quatre autres opérateurs sont des opérateurs binaires, c'est-à-dire qu'ils agissent sur deux valeurs pour produire un résultat. Les trois premiers sont classiques et représentent respectivement l'addition, la soustraction et la multiplication de deux valeurs. On les note avec les signes +, - et *. Voici un exemple utilisant ces trois opérateurs :

Remarquez que pour afficher le résultat des trois opérations x + y, x - y et x * y, on a utilisé la balise %d. Cela signifie que le résultat de l'opération est de type int. Et cela s'explique par le fait que les deux variables x et y sont toutes les deux de type int. On reviendra plus en détails sur les types des résultats des différentes opérations plus loin dans ce livre. L'exécution de cet exemple affiche à l'écran :

x + y = 5
x - y = 19
x * y = -84

Il reste deux opérateurs arithmétiques qu'on examine à part car ils se comportent différemment selon qu'on les applique sur des nombres entiers ou des nombres flottants. Il s'agit de l'opérateur de division qui se note / et de l'opérateur modulo qui se note %.

Division entière et reste de la division entière

Commençons par le cas où on applique ces opérateurs sur des nombres entiers. Dans ce cas, l'opérateur de division se comporte comme la division entière et le modulo correspond au reste de la division entière.

Pour comprendre cela, examinons par exemple la division de 5 par 3. Si on peut diviser ces deux nombres de manière exacte, le résultat serait 7/3 = 2,333..., c'est ce qu'on appelle la division exacte. La division entière quant à elle doit produire un résultat qui est un nombre entier, et elle ne garde donc que la partie entière de la division exacte. La division entière 7/3 donne donc comme résultat 2. La division entière n'étant pas exacte, il y a un reste qui correspond à la partie derrière la virgule, à savoir 0,333... pour notre exemple. Si on en revient au calcul fait au départ, à savoir diviser 7 par 3, on peut dire que le résultat vaut 2 et qu'il reste 1 qu'on n'a pas su diviser (1/3 valant 0,333...). On peut dès lors écrire la relation suivante :

$$7 = 2 \times 3 + 1$$

Voici donc comment effectuer une division entière et calculer le reste de cette division entière en C :

Lors de l'exécution de ces instructions, puisque les variables x et y sont de type int, les opérateurs / et % vont respectivement calculer la division entière et le reste de la division entière. On pourra donc voir à l'écran :

7 = 2 x 3 + 1

De manière générale, lorsqu'on divise un dividende $w$ par un diviseur $d$, on obtient deux résultats : le quotient $q$ et le reste $r$, avec la propriété que $r < y$ et tel que la formule $D = qd + r$ soit respectée. La figure 12 résume cela.

Division entière
Résultats obtenus lors de la division $D/d$, deux nombres entiers.

Une autre façon de comprendre l'opérateur modulo entre deux valeurs $x$ et $y$ est qu'il va soustraire $y$ de $x$, plusieurs fois d'affilée tant qu'il peut, jusqu'à ce que ce qu'il reste soit inférieur à $y$.

Dans notre exemple, on veut donc diviser 7 par 3. Si on retire une fois 3, on trouve 4. On peut retirer une seconde fois 3, et il restera 1. À ce moment, on ne peut plus retirer 3, le calcul est donc terminé. Le résultat de la division entière vaut 2 (on a su retirer deux fois 3) et le reste de la division entière vaut 1 (il reste 1 à la fin).

Division exacte

Dès lors que l'on n'utilise plus l'opérateur / avec deux valeurs de type int, le résultat calculé correspondra à la division exacte. L'exemple suivant affichera la valeur de la division exacte de 7 par 3.

En fait, il suffit qu'une seule des deux valeurs de l'opérateur / soit un nombre flottant pour que la division soit une division exacte. On détaillera cette propriété plus loin dans le livre. L'exemple ci-dessus affiche donc lors de son exécution :

x / y = 2.333333

Qu'en est-il de l'opérateur modulo appliqué à des nombres flottants ? C'est tout simple, il n'est pas applicable pour des nombres flottants.

Parité et divisibilité

À quoi peut bien servir l'opérateur modulo en pratique ? Nous allons voir maintenant quelques exemples d'utilisation.

Une première utilisation que l'on peut faire est de tester la parité d'un nombre entier, c'est-à-dire identifier si un nombre premier est pair ou impair. Pour cela, si l'on suit la définition de parité, il suffit de vérifier si le nombre que l'on veut tester est divisible par 2 ou non. Testons par exemple la parité du nombre entier 17 :

La variable parity va donc contenir le reste de la division entière de 17 par 2. Étant donné que 17 est impair, il restera 1 et l'exécution du code affichera :

Parité de 17 : 1

Le résultat d'une opération x % 2 vaudra en fait toujours soit 1 soit 0. Si le nombre entier x est pair, la valeur sera 0 et s'il est impair, ce sera 1. De manière générale, lorsqu'on calcule x % N, cela produira toujours une valeur comprise entre 0 et $N - 1$. On peut également tester la divisibilité d'un nombre x par un autre nombre d grâce à l'opérateur modulo. Si le résultat vaut 0, c'est que x est divisible par d, sinon il ne l'est pas.

Ramener un nombre entier dans un intervalle

Enfin, voyons un dernier exemple d'utilisation de l'opérateur modulo. On peut l'utiliser pour ramener un nombre dans un intervalle donné. Prenons par exemple les heures de la journée dont on sait qu'elles sont toujours comprises entre 0 et 23. Donc, si on possède une variable contenant une heure, on peut calculer le modulo 24 pour ramener la valeur entre 0 et 23.

Voyons un exemple :

La première instruction déclare une variable hour initialisée à 21. Il est donc initialement 21 heures. On calcule ensuite l'heure qu'il sera 5 heures plus tard. Pour cela, on calcule et stocke hour + 5 dans la variable result à la deuxième instruction. À ce moment, la variable result contient 26. Cette valeur n'étant pas une heure valide, il faut la ramener entre 0 et 23 et c'est pour cela qu'on affiche result % 24 dans la dernière instruction, pour donc avoir :

Il est maintenant 2 heures

De manière générale, on peut vouloir ramener un nombre entier dans un intervalle $[min; max]$. La figure 13 illustre les différentes étapes qu'il faut suivre pour ce faire. La première étape consiste à ramener la valeur entre 0 et $max - min$. Une fois cela fait, on effectue un décalage vers la droite de $min$ pour ainsi se retrouver entre $min$ et $max$.

Intervalle
Ramener un nombre entier dans l'intervalle $[min; max]$.

Pour la première étape, on va utiliser l'opérateur modulo qui permet de ramener un nombre dans un intervalle $[0; N - 1]$ en calculant le modulo $N$. Pour notre cas, on doit donc calculer le modulo par $max - min + 1$. Ensuite, il suffira d'ajouter la valeur de $min$ au résultat obtenu. Voyons un exemple :

Dans cet exemple, on a fait le calcul par étapes détaillées, mais comme on verra plus loin dans ce livre, on pourra écrire les expressions de manière bien plus compacte. L'exécution de ces instructions produit le résultat suivant :

21 dans [2; 5] vaut 3

Opérateur de comparaison

Un autre type d'opérations que l'on peut vouloir faire consiste à comparer deux valeurs, ce qu'on fait avec des opérateurs de comparaison. On teste si deux valeurs sont les mêmes avec == et si deux valeurs sont différentes avec !=. Le résultat de ces opérations est soit 1 si la comparaison est positive et 0 si elle est négative.

Il faut faire très attention à ne pas confondre = qui est l'opérateur d'affectation, qui permet d'initialiser ou modifier une variable, avec == qui est l'opérateur de comparaison qui teste l'égalité.

On peut également tester si une valeur est strictement plus petite ou plus grande qu'une autre, respectivement avec < et >, et si une valeur est plus petite ou égale avec <= ou plus grande ou égale avec >=. Le résultat calculé par ces opérateurs est de nouveau soit 1 ou 0.

Ces six opérateurs de comparaison permettent en fait de construire des conditions. Une condition est une expression qui peut ne prendre que deux valeurs différentes. On dit souvent qu'une condition est soit vraie soit fausse. On peut associer la valeur 1 au cas vrai et la valeur 0 au cas faux. Comme on le verra au chapitre 4, les conditions sont très importantes pour les instructions de contrôle.

Opérateur logique

Les opérateurs de comparaison permettent de construire des conditions simples. Parfois, on souhaite créer des conditions composées comme par exemple « age est strictement plus grand que 18 et height est plus grand ou égale à 140 ».

Les opérateurs logiques permettent de combiner des conditions pour en créer de nouvelles. Le premier opérateur logique est un opérateur unaire qui permet d'inverser la valeur d'une condition. Il s'agit du NON logique qui se note ! et se place devant la condition à inverser.

La condition 5 < 7 est fausse et l'expression vaut donc 1. Si on utilise le NON logique et qu'on écrit !(5 < 7), on obtient la valeur 0 puisqu'il s'agit de la condition 5 < 7 inversée. La figure 14 représente la table de vérité du NON logique. Une table de vérité reprend les valeurs d'une condition composée sur base des valeurs des conditions élémentaires sur lesquelles l'opérateur est appliqué.

a !a
0 1
1 0
Table de vérité du NON logique.

Les deux autres opérateurs logiques sont des opérateurs binaires. Le premier est le ET logique et fait en sorte de créer une condition composée qui n'est vraie que si les deux conditions élémentaires sont vraies en même temps. Le résultat est faux dans tous les autres cas. Il se note &&. Le second opérateur est le OU logique qui fait en sorte de créer une condition composée qui est vraie si au moins l'une des deux conditions élémentaires est vraie. Il se note ||. La figure 15 reprend les tables de vérité de ces deux opérateurs.

a b a && b a || b
0 0 0 0
0 1 0 1
1 0 0 1
1 1 1 1
Tables de vérité du ET logique et du OU logique.

Voyons un exemple d'utilisation de ces deux opérateurs. La première condition teste que les deux conditions age > 18 et height >= 140 sont toutes les deux vraies en même temps. La seconde condition teste que soit la condition x > 12, soit la condition y <= 10, est vraie, ou que les deux sont vraies en même temps.

Comme vous l'aurez remarqué, les conditions élémentaires ont été placées entre parenthèse. Comme on le verra dans la section suivante, ce n'est pas toujours obligatoire, mais on les a mises ici pour rendre le code plus lisible et compréhensible.

Bien entendu, on peut combiner plus que deux conditions ensemble. Par exemple, écrivons une condition qui vérifie qu'une variable x est soit strictement négative, soit comprise entre 5 et 10. Ici, le rôle des parenthèses est important, et on ne pourrait pas toutes les supprimer.

La figure 16 détaille cette expression pour mettre en évidence la combinaison des conditions avec les opérateurs && et ||.

Condition complexe
Condition complexe combinant un OU logique et un ET logique.

Simplification de condition

Il n'y a pas qu'une seule façon d'écrire une condition. Par exemple, les conditions reprises ci-dessous sont toutes équivalentes, c'est-à-dire qu'elles auront la même valeur pour des valeurs identiques de x.

Il y a plusieurs règles que l'on peut appliquer pour simplifier des conditions. Le premier ensemble de règle concerne l'élimination du NON logique. On a les équivalences suivantes :

! (x == y) x != y ! (x != y) x == y
! (x >= y) x < y ! (x > y) x <= y
! (x <= y) x > y ! (x < y) x >= y

De plus, deux négations successives s'annulent. Ainsi, la condition !!x est simplement équivalente à x. Enfin, on peut également utiliser les lois de De Morgan pour transformer des ET logique en OU logique et inversement.

! (x && y) (!x) || (!y)
! (x || y) (!x) && (!y)

Priorité et associativité des opérateurs

Terminons cette section sur les opérateurs par deux notions importantes : la priorité et l'associativité. Lorsque plusieurs opérations différentes sont utilisées dans la même expression, comment cette dernière est-elle calculée ? Prenons par exemple l'expression suivante :

Cette expression peut être calculée de deux manières différentes qui vont produire un résultat différent. Si on calcule (2 + 3) * 4, on obtient 20, mais si on calcule 2 + (3 * 4), on obtient alors 14.

Il est donc très important de savoir dans quel ordre les opérateurs sont appliqués, ou alors il faut systématiquement mettre des parenthèses partout, pour rendre cet ordre explicite.

Les opérateurs sont en réalité classés selon un ordre de priorité. Les opérateurs les plus prioritaires sont appliqués avant les moins prioritaires. La figure 17 reprend les différents opérateurs qu'on a déjà rencontré, classés par ordre de priorité (les opérateurs de priorité 1 étant les moins prioritaires).

Priorité Opérateurs
14 !, - (changement de signe)
13 *, /, %
12 +, -
10 >, <, >=, <=
9 ==, !=
5 &&
4 ||
Priorité des opérateurs en C.

Enfin, que se passe-t-il lorsque plusieurs opérateurs ayant le même niveau de priorité se retrouvent dans la même expression ? Prenons un exemple :

Cette expression peut être calculée comme (1 - 2) - 3 dans lequel cas elle vaut -4 ou alors elle peut être calculée comme 1 - (2 - 3) dans lequel cas elle vaut 2.

En fait, ce qu'il faut savoir, c'est si on fait les opérations de gauche à droite ou de droite à gauche. Ce concept s'appelle l'associativité des opérateurs. La plupart des opérateurs sont associatifs de gauche à droite. C'est le cas pour la soustraction et la valeur de 1 - 2 - 3 est donc -4. Parmi les opérateurs qu'on a déjà vus, seuls ! et - (changement de signe) sont associatifs de droite à gauche.