Maintenant que nous savons ce que sont les variables, quels sont les différents types arithmétiques, que nous avons appris comment en déclarer et y mémoriser des données, et que nous sommes en mesure d'afficher le contenu d'une de ces variables, nous pouvons à présent étudier les opérateurs arithmétiques, et bits à bits, permettant de travailler avec, et sur, ces variables.
Tout d'abord, nous étudierons les opérateurs arithmétiques
les plus souvent utilisés, ceux permettant d'additionner, diviser,
... Puis, nous verrons ceux qu'il est moins fréquent de rencontrer,
mais dont l'utilité ne peut être niée. Naturellement,
nous n'oublierons pas de faire mention des formes concises de ces opérateurs.
Ensuite, après un bref rappel sur la logique booléenne,
nous étudierons les opérateurs bits à bits, et les
opérateurs de décalage de bits.
Pour finir, nous résumerons les règles de priorité
entre les différents opérateurs que nous aurons ici étudié.
Puisque l'on dispose de variables à même de mémoriser des valeurs, il apparait comme nécessaire de pouvoir manipuler ces valeurs : les additionner, les soustraire, ou leur faire subir des traitements plus... exotiques.
Le C utilise les mêmes opérateurs que les mathématiques pour les quatre opérations standards :
En plus de ceux-ci, un cinquième opérateur est défini, qui permet de calculer le modulo, c'est-à-dire le reste de la division entière entre deux nombres. Le symbole utilisé pour cela est le pourcent : %
Ces cinq opérateurs sont des opérateurs binaires. Cela signifie qu'ils travaillent avec deux éléments, un à leur gauche, et un à leur droite.
Voici un petit exemple d'utilisation de ces opérateurs :
Comme nous pouvons le remarquer, on peut utiliser ces opérateurs avec des variables, ou directement sur des valeurs ; cela revient au même. Cela dit, on utilise la plupart du temps des variables, contenant le résultat d'appels de fonctions, par exemple : un code statique, faisant toujours la même chose, ne servirait pas à grand chose, en dehors des exemples !
Naturellement, ces opérateurs peuvent être séparé des variables par des espaces, des tabulations, des retours à la ligne... enfin, tout ce qui vous semble améliorer la lisibilité, en particulier dans le cas d'expressions longues et complexes !
Les quatre opérateurs "standard" peuvent travailler
aussi bien avec des entiers qu'avec des flottants (notez que la division
de deux entiers est en fait une division entière : elle renvoie
le quotient, arrondi à la valeur inférieure (par exemple,
7/4 renverra un entier valant 1, et non pas un flottant valant 1.75)).
L'opérateur modulo, lui, ne peut travailler qu'avec des nombres
entiers.
Notez qu'une division, ou un modulo, par 0 a un résultat non défini par la norme ; sur nos calculatrices, une division par 0 entraîne un plantage.
Comme en mathématiques, encore une fois, il est possible d'altérer
la priorité des opérateurs, en utilisant des parenthèses.
En premier sera évalué ce qui est dans les parenthèses
les plus internes.
Par exemple, 3+2*2 donnera 3+(2*2), soit 3+4, soit 7
Alors que (3+2)*2 donnera 5*2, soit 10.
Il est, naturellement, possible d'imbriquer plusieurs niveaux de parenthèses.
D'ailleurs, lorsque vous travaillez avec des expressions complexes, je vous conseille d'utiliser des parenthèses pour clarifier votre code ; elles permettront qu'au premier coup d'oeil, on comprenne l'ordre d'évaluation, sans avoir à réfléchir sur les priorités d'opérateurs !
Il nous arrive parfois d'effectuer une opération sur une variable,
et d'affecter le résultat de ce calcul dans cette variable...
Par exemple, nous pourrions penser à une écriture de ce
type :
Ceci peut être écrit de façon plus concise, en utilisant la syntaxe présentée ci-dessous :
En fait, la plupart des opérateurs arithmétiques admettent
une syntaxe de ce genre :
Une expression de la forme "A = A opérateur (B);" équivaut
à "A opérateur= B", mais à part le fait
que A ne sera évalué qu'une fois.
Les cinqs opérateurs binaires +, -, *, / et % que nous avons ici
étudié admettent cette forme, et les cinq opérateurs
binaires de manipulation de bits que nous verrons plus bas au cours de
ce chapitre l'admettent aussi. Notez que les opérateurs unaires,
ceux ne travaillant que sur une seule variable, que nous allons voir juste
au-dessous, ne peuvent pas utiliser une syntaxe concise de la sorte !
Encore une fois, j'insiste sur le fait que le C est un langage concis, et que cette particularité est particulièrement appréciée par ses utilisateurs ; il est donc certain que vous rencontrerez ces formes si vous parcourez des codes sources, et, donc, il serait utile que vous les reteniez...
Il existe deux opérateurs, en C, permettant de forcer le signe d'une valeur. Ce sont tous deux des opérateurs unaire, ce qui signifie qu'ils ne travaillent que sur une donnée.
Pour obtenir l'opposé d'une valeur, on utilise celle-ci :
Notez que cela revient à soustraire la valeur à 0... mais en l'écrivant de façon plus propre.
De façon symétrique, l'opérateur + unaire a été
défini. Il renvoie la valeur de l'opérande sur laquelle
il travaille.
Notez qu'il ne renvoie pas la valeur absolue ! Utiliser l'opérateur
+ unaire sur une valeur négative renverra... une valeur négative
!
Voici un petit exemple illustrant l'utilisation de ces deux opérateurs, sur une valeur positive, et sur une donnée négative :
Si vous exécutez cet exemple, prêtez tout particulièrement attention à la seconde ligne affichée ! Et retenez en mémoire la remarque que j'ai fait juste avant de présenter ce code source !
Lorsqu'il s'agit d'ajouter un à une valeur, c'est-à-dire
de l'incrémenter, ou de lui retrancher un, c'est-à-dire
la décrémenter, il est possible d'utiliser respectivement
les opérateurs ++, ou -- (deux plus à la suite, ou deux
moins à la suite).
Ces opérateurs peuvent être placés avant, ou après,
leur opérande. Dans le premier cas, on parlera d'incrémentation
préfixée, et, dans le second, d'incrémentation postfixée.
Lorsque l'on utilise un opérateur postfixé, la valeur de
la variable nous est renvoyée, et, seulement ensuite, elle est
incrémentée (ou décrémentée, selon
le cas).
Lorsque l'on travaille avec un opérateur préfixé,
la variable est incrémentée (ou décrémentée),
et, ensuite, sa valeur nous est renvoyée.
Pour que vous compreniez bien ce qui se passe, et comment, voici un exemple :
Ce que je vous recommande de faire est de l'exécuter, et de suivre en parallèle le résultat de son exécution et son code source.
Il est tout à fait possible d'utiliser des écritures telles que celle-ci sans que le compilateur ne s'en offusque :
Ou que celles-là :
Ces écritures nous montrent bien que l'on peut mettre un paquet
de plus ou de moins à la suite... tout en sachant qu'il faut, par
endroit, utiliser des espaces ou des parenthèses, pour que le compilateur
soit à même de faire la différence entre les opérateurs
d'incrémentation, d'addition, et de signe...
Cela dit, c'est assez peu lisible ! Je vous conseille donc de bannir totalement
ce genre de syntaxe, et de préférer étaler ceci sur
plusieurs lignes, que vous n'aurez pas de mal à relire ! Parce
que pour comprendre ces expressions, c'est quelque peu complexe, et on
ne peut s'empêcher d'hésiter quand à l'ordre dans
lequel les opérations seront menées (notamment, a sera incrémenté
avant, ou après de faire la somme ? Je dois reconnaître que je
suis incapable de le dire avec certitude... si j'ai bonne mémoire,
la norme ne le dit même pas, et on se retrouve dans la même
situation que celle que je présente en dessous)
Pour l'écriture suivante, je suis sûr de moi : elle est totalement indéfinie par la norme (ce n'est pas la seule, mais c'est la plus évidente) :
L'ordre d'évaluation n'est pas clairement défini, et on
ne sait pas dans quel ordre les incrémentations et la somme seront
effectuées...
D'ailleurs, GCC vous préviendra que L'opération concernant
a peut être indéfinie.
Dans un programme, c peut au final valoir une certaine valeur... et dans
un autre programme, une autre valeur !
Ce type d'écriture, présentant ce genre d'effets de bords
est à bannir !
En plus des opérateurs arithmétiques usuels, que nous utilisons
tout le temps, le C définit des opérateurs travaillant sur les
bits. Certains de ces opérateurs permettent de travailler bit à
bit sur des valeurs ; d'autres permettent de décaler, vers la droite
ou la gauche, les bits d'une donnée.
Nous commencerons cette partie par un bref rappel de logique booléenne,
puis nous présenterons les opérateurs correspondants.
La logique booléenne n'utilise que deux valeurs, 0, et 1, qui correspondent
tout à fait à ce que l'on emploie lorsque l'on travaille en binaire.
Trois opérations, voire quatre, d'algèbre de Boole nous seront
utiles lorsque nous programmerons en C : le OU (or, en anglais), le OU exclusif
(xor, ou eor, selon les habitudes du programmeur ; en C, on utilise généralement
xor, alors qu'en Assembleur, on aura plus tendance à utiliser eor, par
exemple ; mais ce sont deux appellations qui renvoient à la même
chose), et le ET (and). En plus de cela, il est possible de définir le
NON (not).
Voici les tables de ces opérations :
a | b | a OR b | a XOR b | a AND b | NOT a | ||||
---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 1 | ||||
0 | 1 | 1 | 1 | 0 | 1 | ||||
1 | 0 | 1 | 1 | 0 | 0 | ||||
1 | 1 | 1 | 0 | 1 | 0 |
a OR b vaut 1 si a, ou b, ou les deux, valent 1.
a XOR b vaut si a ou b, mais pas les deux à la fois, valent 1.
a AND b vaut 1 si a et b valent tous les deux 1.
Et NOT a vaut 1 si a vaut 0, et, inversement, 0 si a vaut 1.
Le C propose des opérateurs bits à bits permettant de faire ceci directement sur les bits composant une, ou deux, valeurs, selon l'opérateur. Notez que ces opérateurs ne travaillent qu'avec des valeurs, on des variables, de type entier : ils ne peuvent pas être utilisés sur des flottants !
Pour effectuer un OR bit à bit, vous utiliserez l'opérateur |
(le pipe sous les systèmes UNIX, accessible par Alt-Gr 6 sur un clavier
azerty).
Pour le XOR, il convient d'utiliser le ^ (accent ciconflexe).
Pour le AND, c'est un & qu'il revient d'écrire (é commercial,
touche 1 du clavier azerty).
Et enfin, pour le NOT, c'est le ~ (tilde, soit Alt-Gr 2).
Notez que |, ^, et & sont des opérateurs binaires, alors que le ~ est un opérateur unaire. Les trois premiers, en tant qu'opérateurs binaires, disposent d'une écriture abrégée pour les affectations, telle &=, par exemple.
Ci-dessous, un extrait de code source présentant quelques emplois de ces quatre opérateurs :
Je reconnais qu'il est assez rare pour un débutant d'employer ces opérateurs, mais ils sont souvent fort utiles pour certains types de programmes ; ne voulant pas avoir à revenir dessus plus tard, au risque de les oublier, j'ai préféré les présenter dans ce chapitre.
Un autre type permet de manipuler directement les bits d'une variable, ou d'une donnée. Ils s'agit des deux opérateurs de décalage de bits.
Le premier, >> (deux fois de suite le signe supérieur) permet de décaler les bits d'un nombre vers la droite, et le second, << (deux signes inférieur successifs), est sa réciproque, c'est-à-dire qu'il décale vers la gauche.
Ces opérateurs s'utilisent de la façon suivante :
valeur OP N
Où valeur est une donnée, une variable, une valeur, ..., et N
le nombre de bits dont on souhaite décaler les bits de la donnée.
Notez que décaler de N bits vers la droite revient à diviser par
2 puissance N, et décaler de N bits vers la gauche correspond à
une multiplication par 2 puissance N.
Voici un petit exemple, pour vous donner la syntaxe d'utilisation, en clair :
La remarque faite plus haut pour les opérateurs bits à bits est ici aussi valable.
Pour terminer ce chapitre, je pense que récapituler les priorités
des opérateurs que nous avons ici vu peut être une très
bonne chose...
C'est pour ce genre de choses qu'un livre utilisé comme référence
peut être une bonne chose : même si aucun livre ne traite de la
programmation en C pour TI, le C pour PC et le C pour TI sont le même
langage ! Moi-même, malgré plusieurs années d'expérience
en programmation en langage C, j'ai préféré me référer
à un livre pour écrire cette partie, afin de ne pas écrire
quoi que ce soit d'erroné.
Dans le doute, il demeure possible d'utiliser des parenthèses pour forcer
un ordre de priorité ; je vous conseille d'en utiliser assez souvent,
dès que les choses ne sont pas tout à fait certaines dans votre
esprit, afin de rendre votre source plus clair, et plus facile à comprendre
si vous êtes un jour amené à le relire ou à le distribuer...
Le tableau ci-dessous présente les différents opérateurs que nous avons vu (j'espère ne pas en oublier, ni en rajouter que nous n'ayions pas encore étudié). La ligne la plus haute du tableau regroupe les opérateurs les plus prioritaire, et les niveaux de priorité diminuent au fur et à mesure que l'on descend dans le tableau. Lorsque plusieurs opérateurs sont sur la même ligne, cela signifie qu'ils ont le même niveau de priorité.
Opérateurs, du plus prioritaire au moins prioritaire |
---|
() |
~ ++ -- + - sizeof |
* / % |
+ - |
<< >> |
& |
^ |
| |
= += -= *= /= %= &= ^= |= <<= >>= |
Notez que les opérateurs + et - unaires sont plus prioritaires que leurs
formes binaires.
N'oubliez pas que le C n'impose pas l'ordre dans lequel les opérandes
d'un opérateur sont évaluées, du moins, pour les opérateurs
que nous avons jusqu'à présent vu ; le compilateur est libre de
choisir ce qu'il considère comme apportant la meilleure optimisation.
Il en va de même pour l'ordre d'évaluation des arguments passés
à une fonction.
Maintenant que nous en avons terminé avec tous ces opérateurs,
nous allons passer à un chapitre qui nous permettra d'étudier
ce que sont les structures de contrôles, lesquelles sont proposées
en C, et comment les utiliser.
Cependant, avant de pouvoir nous lancer dans le vif du sujet, il nous faudra
étudier encore quelques nouveaux opérateurs, qui nous seront indispensables
pour la suite...