Next: 3 Structures de contrôle
Up: Le langage C
Previous: 1 Eléments de base
Une expression est un objet syntaxique obtenu en assemblant ``correctement'' des constantes, des variables et des opérateurs. Par exemple, l'expression x +3 est une expression. Dans le langage C, il y a bien d'autres opérateurs que les opérateurs arithmétiques qu'on a l'habitude de manipuler; il y en a, en fait, plus de quarante opérateurs.
Il nous est naturel d'écrire l'addition de deux nombres sous la forme a + b .
Cela vient de notre culture mathématique. Cette notation n'est qu'une convention choisie par nos ancêtres; ce n'est pas la seule possible. Nos ancêtres auraient très bien pu convenir que cette addition se noterait + ab ou bien ab + .
Notation | Nom |
a + b | infixé |
+ ab | préfixée ou polonaise |
ab + | postfixée ou polonaise inversée |
L'avantage des notations préfixée et postfixée est qu'elles ne requierent pas de parenthèses. Par exemple, l'expression ( a + b )* c se note *+ abc en notation préfixée et ab + c* en notation postfixée. Plus concise, ces notations (préfixée et postfixée) sont plus adaptées pour les machines que pour nous; on y perd en clarté. Aussi, le langage C utilise la notation infixée pour les opérateurs et on y trouve
!
expr.
?:
) et
qui se note
op1 ? op2 : op3
(voir section 2.2.8).
Nous avons l'habitude de manipuler des expressions (par exemple arithmétiques) et il nous est relativement aisé de préciser exactement le sens des expressions comme : 2+3*4*5-2, 2-3-4 etc...
On sait que ces expressions sont équivalentes à (2+(3*(4*5)))-2, (2-3)-4. Introduire les paranthèses permet de définir sans ambiguïté l'expression que l'on manipule. A priori, c'est notre culture mathématique qui nous permet de parenthéser ces expressions. Pour éviter l'usage des parenthèses qui alourdissent la lecture, lorsque cela est posible, les mathématiciens ont fixé des règles pour que tout le monde parenthèse (dans sa tête) de la même manière toute expression ambigüe.
Par exemple, dans l'expression 2+3*4, la sous expression 3*4 est évaluée en premier et le résultat obtenu est ajouté à la valeur 2 (forme parenthésée : 2 + ( 3 * 4)). On dit que l'opérateur * possède une priorité supérieure à la priorité de l'opérateur +.
De même, dans l'expression 2-3-4, la sous expression 2-3 est évaluée en premier et, au résultat obtenu, on soustrait la valeur 4 (forme parenthésée : (2 - 3) - 4). On dit que l'ordre (d'évaluation) de l'opérateur - est de gauche à droite.
Comme nous le verrons plus loin, outre les expressions arithmétiques, le langage C dispose de beaucoup d'autres expressions. La donnée d'une priorité et d'un ordre d'évaluation permet de fixer des règles communes d'évaluation des expressions.
Ces priorités et ordre d'évaluation ne permettent évidemment pas de se dispenser complètement des parenthèses. En effet, on utilise les parenthèses lorqu'on veut évaluer une expression d'une manière autre que celle définie à l'aide de la priorité et de l'ordre d'évaluation. Par exemple, si l'on veut faire 2+3 et multiplier le résultat par 4, on notera (2+3)*4 et il n'est pas possible de l'écrire sans les paranthèses avec la notation infixée (sauf à développer l'expression).
Comme premier exemple d'opérateur,
voici les caractéristiques de l'opérateur ()
Opérateur | Nom | Notation | Priorité | Ordre |
() |
parenthèses | (...) | 15 | gauche-droite |
=
; nous l'avons déjà rencontré plus haut
(voir section 1.6).
Le symbole=
peut prêter à confusion. Il ne s'agit pas d'une équation au sens mathématique du terme. Il s'agit tout simplement de remplir une case mémoire avec une certaine valeur.Le système d'équations {x=2, x=3} n'a pas de solution au sens mathématique; par contre les deux affectations successives
x=2; x=3
est parfaitement valideet correspond à ranger la valeur 2 puis la valeur 3 dans la zone mémoire allouée à la variable x . De même, l'équation {x=x+1} n'a pas de solution au sens mathématique; par contre l'affectations
x=x+1
est parfaitement valide et consiste à incrémenter de 1 la valeur de la variable x .
Comme l'affectation range une valeur dans une variable (une zone mémoire), il est impératif que le membre gauche d'une affectation représente une zone mémoire : c'est ce qu'on appelle une lvalue (pour left value) . Une constante n'est pas une lvalue car il ne désigne pas l'adresse d'une zone mémoire. Elle ne peut donc pas figurer en membre gauche d'une affectation.
Le membre droit d'une affectation peut désigner soit une constante
soit une zone mémoire soit une expression quelconque : l'affectation
x=2
range la valeur 2 dans la variable x , l'affectation
x=y
range dans la variable x le contenu de la variable y et
l'affectation x=y+1
range dans la variable x le contenu de la
variable y incrémenté de 1.
Opérateur | Nom | Notation | Priorité | Ordre |
= |
Affectation | x = y | 2 | droite-gauche |
L'affectation est une expression.
La valeur d'une affectation est la valeur de son membre gauche après exécution
de l'affectation. Si la variable y contient la valeur 100 alors
l'affectation x=y+20
a pour valeur 120.
Une affectation peut figurer en membre droit d'une autre affectation.
L'affectation x=y=1
est parfaitement valide car elle représente (voir ordre
d'évaluation) l'affectation x=(y=1)
. Puisque y=1
est une expression,
elle peut figurer donc en membre droit d'une affectation.
Puisque elle est syntaxiquement juste, quel sens donner à cette affectation ?
Puisque y=1
est une expression dont la valeur est la valeur de son membre gauche
(ici la valeur entiere 1), l'affectation x=y=1
range dans la variable x
la valeur 1 après avoir rangé dans y cette même valeur 1 .
Ainsi, l'affectation x1=x2=x3=...=x20=0
est
équivalent à la suite d'affectations (dans l'ordre indiqué) x20=0
,
x19=0
,
x18=0
, ..., x1=0
.
Opérateur | Nom | Notation | Priorité | Ordre |
+ |
Addition | x + y | 12 | gauche-droite |
- |
soustraction | x - y | 12 | gauche-droite |
* |
multiplication | x * y | 13 | gauche-droite |
/ |
division | x / y | 13 | gauche-droite |
% |
modulo | x % y | 13 | gauche-droite |
+
, -
, *
fonctionnent comme on s'y attend.
Par contre, l'opérateur /
se comporte de manière différente selon
que les opérandes sont des entiers ou des nombres flottants.
Lorsqu'il s'agit de nombres flottants, le résultat est un nombre flottant
obtenu en divisant les deux nombres (pas de surpise !). Lorsqu'il s'agit
de nombres entiers, le résultat est un nombre entier obtenu en calculant
la division entière.
L'opérateur %
n'est défini que pour les entiers et le résultat est
le reste de la division entière des opérandes.
Rappel sur la division entière : On appelle quotient et reste de la division entière de a et de b , les nombres entiers q et r vérifianta = q * b + r; 0 < r < b
Exemple : Si a =20 et b =3 alors q =6 et r =2 (20 = 6 * 3 + 2).
Comme nous l'avons déjà vu, on peut appliquer des opérateurs arithmétiques
sur les caractères (qui ne sont qu'un type d'entier particulier).
On est donc autorisé à écrire, 'A'-'0'
, 'A'+1
, etc...
L'opérateur unaire -
a une priorité supérieure aux opérateurs
binaires arithmétiques.
Opérateur | Nom | Notation | Priorité | Ordre |
- (unaire) |
négation | - x | 14 | droite-gauche |
L'opérateur unaire +
n'existe pas en C. Ce n'est qu'une notation et ce
symbole est supprimé lors de l'analyse syntaxique.
Opérateur | Nom | Notation | Priorité | Ordre |
== |
test d'égalité | x == y |
9 | gauche-droite |
!= |
test de non-égalité | x != y |
9 | gauche-droite |
<= |
test d'inférieur ou égal | x <= y |
10 | gauche-droite |
>= |
test de supérieur ou égal | x >= y |
10 | gauche-droite |
<= |
test d'inférieur strict | x < y |
10 | gauche-droite |
>= |
test de supérieur strict | x > y |
10 | gauche-droite |
Théoriquement, le résultat d'une compraison est une valeur boolénne (vrai ou faux). Dans le langage C, le résultat d'une comparaison est 1 ou 0 selon que cette comparaison est vraie ou fausse.
Comme nous l'avons déjà signalé,
il n'existe pas de type booléen en C; la valeur entière 0
sera considérée comme équivalente à la valeur faux
et toute valeur différente de 0
équivalente à la valeur vrai
.
Puisqu'une valeur boolénne est une valeur entière, on peut utiliser des expressions suivantes![]()
2
,0
,x+2
,x=2
, etc... comme si elles étaient des résultats de comparaison.
Attention !!!
Ne confondez pas==
(test d'égalité) et=
(affectation).
Cette confusion (souvent involontaire) est source de nombreuses erreurs. Cette confusion est d'autant plus facile à faire que les deux écritures sont syntaxiquement correctes :
if (x == 2) { ... |
if (x = 2) { ... |
x=2
qui vaut 2 quelle que soit la valeur de x
(dans cet exemple). Ainsi, selon que la valeur contenue dans le
varaible x est 2 ou pas, la première forme fera des choses
différentes. Par contre, quelque soit la valeur de x , la valeur de
l'expression x=2
est toujours 2.
On construit la table de vérité pour chacun des connecteurs :
a |
|
~ a |
vrai | faux | |
---|---|---|
faux | vrai | |
Dans le langage C, on dispose de ces connecteurs (sous une syntaxe particulière) et on peut fabriquer des expressions avec celles-ci. La valeur d'une expression boolénne est, comme le résultat des comparaisons, une valeur entière.
Opérateur | Nom | Notation | Priorité | Ordre |
&& |
ET | x && y |
5 | gauche-droite |
|| |
OU | x || y |
4 | gauche-droite |
! (unaire) |
NON | ! x |
14 | droite-gauche |
(x >= 2) || ( x >= y)
s'arrête avant même d'avoir évalué
l'expression x >= y
,
puisque (x >= 2)
est vrai d'où on peut conclure que
la valeur
de l'expression (x >= 2) || ( x >= y)
est également vraie.
Cette remarque a son importance dans deux cas:
(x >= 2) || ( x = x+1)
Opérateur | Nom | Notation | Priorité | Ordre |
& |
ET bit à bit | x & y |
8 | gauche-droite |
| |
OU bit à bit | x | y |
6 | gauche-droite |
^ |
OU exclisif bit à bit | x ^ y |
7 | gauche-droite |
~ (unaire) |
NON bit à bit | ~ x |
14 | droite-gauche |
>> |
décalage à droite | >> x |
11 | droite-gauche |
<< |
décalage à gauche | << x |
11 | droite-gauche |
Opérateur | équivalent | Notation | Priorité | Ordre |
+= |
x = x + y |
x += y |
2 | droite-gauche |
-= |
x = x - y |
x -= y |
2 | droite-gauche |
.
*= |
x = x * y |
x *= y |
2 | droite-gauche |
/= |
x = x / y |
x /= y |
2 | droite-gauche |
%= |
x = x % y |
x %= y |
2 | droite-gauche |
>>= |
x = x >> y |
x >>= y |
2 | droite-gauche |
<<= |
x = x << y |
x <<= y |
2 | droite-gauche |
&= |
x = x & y |
x &= y |
2 | droite-gauche |
|= |
x = x | y |
x |= y |
2 | droite-gauche |
^= |
x = x & y |
x |= y |
2 | droite-gauche |
++ |
x = x + 1 |
x++ ou ++x |
14 | droite-gauche |
-- |
x = x - 1 |
x-- ou --x |
14 | droite-gauche |
y = x++
est le raccourci pour y = x; x = x + 1;
et
l'expression y = ++x
est le raccourci pour x = x + 1; y = x;
.
Ces opérateurs produisent des effets de bord. Un effets de bord
est la modification d'une opérande lors de l'évaluation d'une l'expression;
dans l'exemple ci-dessus, la variable x
subit une modification
pendant l'affectation de la variable y
.
?: |
opérateur conditionnel | e ? x : y |
3 | droite-gauche |
Exemple: a = (v == 2) ? 1 : 2;
affecte la variable a à la valeur 1 si
v vaut 2, sinon affecte la variable a à la valeur 2.
, |
opérateur séquentiel | e1, e2 |
1 | droite-gauche |
e1, e2
consiste en l'évaluation
successives (dans l'ordre) des expressions e1
puis de e2
.
L'expression e1, e2
possède le type et la valeur de e2
.
Cet opérateur donne l'occupation mémoire (en octets) d'une variable ou d'un type de donné.
sizeof(c)
est 1 si c
est une variable de type char; l'expression sizeof(char)
donne
également la valeur 1.
&
donne l'adresse d'une variable ou d'une
expression qui est une lvalue.
& |
opérateur d'adressage | &x |
14 | droite-gauche |
(type) |
opérateur de conversion | (long int)c |
14 | droite-gauche |
()
permet de définir l'ordre
d'évaluation d'une expression. C'est l'opérateur
que l'on utilise traditionellement.
C'est également ce même opérateur qui est utilisé pour encapsuler les paramètres des fonctions. Même lorsqu'une fonction n'a pas d'arguments, ces paranthèses sont requises.
() |
paranthésage | () |
15 | gauche-droite |
2|c|Définition de fonction | |
---|---|
int fonction2(char c, int i) { .... } |
int fonction0(void) { .... } |
2|c|Appel de fonction | |
{ .... i = fonction2('c', 235); .... } |
{ .... i = fonction0(); .... } |
.
et ->
servent à selectionner des composants de
données structurées. Nous rencontrerons ces opérateurs plus loin dans ce
doucument (voir chapitre 6).
. |
opérateur de selection | x.info |
15 | gauche-droite |
-> |
opérateur de selection | x->info |
15 | gauche-droite |
[] |
opérateur de selection | x[3] |
15 | gauche-droite |
Les conversion implicites sont celles faites automatiquement par un compilateur C lors de l'évaluation d'une expression (et donc également d'une affectation). Comme nous le verrons plus loin, il y a également conversion implicite lors de l'appel de fonction (voir chapitre 4).
long double
, conversion de l'autre
expression en long double
; le résultat est un long double
.
double
, conversion de l'autre
expression en double
; le résultat est un double
.
float
, conversion de l'autre
expression en float
; le résultat est un float
.
char
, short
,
enum
ou champ de bits en int
. Si l'un des types ne peut être
représenté en int
, conversion en unsigned int
.
unsigned long
,
conversion de l'autre expression en unsigned long
;
les résultat est un unsigned long
.
long
et l'autre un unsigned int
,
long
peut représenter toutes les valeurs d'un
unsigned int
, conversion de unsigned int
en long
;
le résultat est un long
.
unsigned long
;
le résultat est un unsigned long
.
long
, conversion de l'autre
expression en long
; le résultat est un long
.
unsigned int
,
conversion de l'autre expression en unsigned int
;
le résultat est un unsigned int
.
int
;
le résultat est un int
.
char
sont convertis en int
.
Mais que se passe-t-il lorsqu'on affecte une expression plus ``longue'' à une expression plus ``courte'' ?
La valeur affectée à la variable c sera tronquée des bits de poids forts. Le conversion d'un
int i; char c;c = i;
float
en un int
, tronque la partie fractionnaire;
mais selon l'implantation, la conversion d'un double
en float
sera arrondie ou tronquée.
Outre les conversion implicites, le programmeur peut effectuer
des conversions à la demande grâce à l'opérateur de converiosn de type
que l'on a déjà rencontré en 2.2.8.
L'expression (int) 1.225
convertit le réel de type double en un entier.
il en découle évidemment une perte de précision selon les
règles que nous avons précisées plus haut; pour cet exemple, la conversion
de (int) 1.225
donne comme résultat la valeur entière 1
.
Opérateur | Nom | Priorité | Ordre | |
[] |
elt de tableau | 15 | gauche-droite | |
() |
paranthésage | 15 | gauche-droite | |
. |
selection | 15 | gauche-droite | |
-> |
selection | 15 | gauche-droite | |
& |
adressage | 15 | gauche-droite | |
sizeof |
dimension | 14 | droite-gauche | |
(type) |
conversion | 14 | droite-gauche | |
! (unaire) |
NON | 14 | droite-gauche | |
~ (unaire) |
NON bit à bit | 14 | droite-gauche | |
++ |
incrément | 14 | droite-gauche | |
-- |
décrément | 14 | droite-gauche | |
* |
multiplication | 13 | gauche-droite | |
/ |
division | 13 | gauche-droite | |
% |
modulo | 13 | gauche-droite | |
+ |
Addition | 12 | gauche-droite | |
- |
soustraction | 12 | gauche-droite | |
>> |
décalage à droite | 11 | droite-gauche | |
<< |
décalage à gauche | 11 | droite-gauche | |
<= |
test d'inférieur ou égal | 10 | gauche-droite | |
>= |
test de supérieur ou égal | 10 | gauche-droite | |
<= |
test d'inférieur strict | 10 | gauche-droite | |
>= |
test de supérieur strict | 10 | gauche-droite | |
== |
test d'égalité | 9 | gauche-droite | |
!= |
test de non-égalité | 9 | gauche-droite | |
& |
ET bit à bit | 8 | gauche-droite | |
^ |
OU exclisif bit à bit | 7 | gauche-droite | |
| |
OU bit à bit | 6 | gauche-droite | |
&& |
ET | 5 | gauche-droite | |
|| |
OU | 4 | gauche-droite | |
?: |
opérateur conditionnel | 3 | droite-gauche | |
= |
Affectation | 2 | droite-gauche | |
+= |
Affectation | 2 | droite-gauche | |
-= |
Affectation | 2 | droite-gauche | |
.
*= |
Affectation | 2 | droite-gauche | |
/= |
Affectation | 2 | droite-gauche | |
%= |
Affectation | 2 | droite-gauche | |
>>= |
Affectation | 2 | droite-gauche | |
<<= |
Affectation | 2 | droite-gauche | |
&= |
Affectation | 2 | droite-gauche | |
|= |
Affectation | 2 | droite-gauche | |
^= |
Affectation | 2 | droite-gauche | |
, |
séquentiel | 1 | droite-gauche |
Touraivane