Types de base, opérateurs & expressions


sommaire du chapitre :

Introduction

1. Les types simples
1a. Les types entiers
1b. Les types rationnels

2. La déclaration des variables simples
2a. Les constantes numériques
2b. Initialisation de variables

3. Les opérateurs standards
3a. Les opérateurs connus
3b. Les opérateurs particuliers du C

4. Les expressions et les instructions

5. Les priorités des opérateurs

6. Les fonctions arithmétiques standard

7. Les conversions de type
7a. Les conversions automatiques
7b. Les conversions forcées

8. Définition de constantes et de macros


Introduction

Les variables et les constantes sont les données principales qui peuvent être manipulées par un programme.
Les déclarations introduisent les variables qui sont utilisées, fixent leur type et parfois aussi leur valeur de départ.
Les opérateurs contrôlent les actions que subissent les valeurs des données.
Pour produire de nouvelles valeurs, les variables et les constantes peuvent être combinées à l'aide des opérateurs dans des expressions.
Le type d'une donnée détermine l'ensemble des valeurs admissibles, le nombre d'octets à réserver en mémoire et l'ensemble des opérateurs qui peuvent y être appliqués.

Motivation

La grande flexibilité de C nous permet d'utiliser des opérandes de différents types dans un même calcul.
Cet avantage peut se transformer dans un terrible piège si nous ne prévoyons pas correctement les effets secondaires d'une telle opération (conversions de type automatiques, arrondissements, etc.).
Une étude minutieuse de ce chapitre peut donc aider à éviter des phénomènes parfois "inexplicables"...

retour au sommaire


1. Les types simples

Ensembles de nombres et leur représentation

En mathématique, nous distinguons divers ensembles de nombres:

l'ensemble des entiers naturels N
l'ensemble des entiers relatifs Z
l'ensemble des rationnels Q
l'ensemble des réels R.

En mathématique, l'ordre de grandeur des nombres est illimité et les rationnels peuvent être exprimés sans perte de précision.
Un ordinateur ne peut traiter aisément que des nombres entiers d'une taille limitée.
Il utilise le système binaire pour calculer et sauvegarder ces nombres.
Ce n'est que par des astuces de calcul et de représentation que l'ordinateur obtient des valeurs correctement approchées des entiers très grands, des réels ou des rationnels à partie décimale infinie.

Les charges du programmeur

Un programmeur utilisant le C n'a pas besoin de connaître tous les détails des méthodes de codage et de calcul, il doit quand même être capable de :

- choisir un type numérique approprié à un problème donné (trouver un optimum de précision, de temps de calcul et d'espace à réserver en mémoire)
- choisir un type approprié pour la représentation sur l'écran
- prévoir le type résultant d'une opération entre différents types numériques (connaître les transformations automatiques de type que le C accomplit lors des calculs)
- prévoir et optimiser la précision des résultats intermédiaires au cours d'un calcul complexe (changer si nécessaire l'ordre des opérations ou forcer l'ordinateur à utiliser un type numérique mieux adapté)

retour au sommaire

1a. Les types entiers

Avant de pouvoir utiliser une variable, nous devons nous intéresser à deux caractéristiques de son type numérique:
1. le domaine des valeurs admissibles
2. le nombre d'octets qui est réservé pour une variable

Le tableau suivant montre les différentes spécificités des types numériques de base en C :

Définition
Description
Domaine Min
Domaine Max
Nombre d'octets
char
caractère
-128
127
1
short
entier court
-32768
32767
2
int
entier
-32768
32767
2
long
entier long
-2147483648
2147483647
4

- Les modificateurs signed/unsigned:
Si on ajoute le préfixe unsigned à la définition d'un type de variables entières, les domaines des variables sont déplacés comme suit:

Définition
Description
Domaine Min
Domaine Max
Nombre d'octets
unsigned char
caractère
0
255
1
unsigned short
entier court
0
65535
2
unsigned int
entier
0
65535
2
unsigned long
entier long
0
4294967295
4

Si on ajoute le préfixe signed à la définition d'un type de variables entières, les domaines des variables restent comme dans le premier tableau...

retour au sommaire

1b. Les types rationnels

En informatique, les rationnels sont souvent appelés des "flottants" (de "en virgule flottante").
Ce terme vient du fait qu'une variable rationnelle est stockée sous la forme suivante :

[+/- <mantisse> * 10 ^ <exposant>]

où <mantisse> représente un décimal positif avec un seul chiffre devant la virgule, et <exposant>, un entier relatif. (par exemple : -1.5 * 10^2, 4.3 * 10^12...)

Le tableau suivant montre les différentes spécificités des types rationnels de base en C :

Définition
Précision
Mantisse
Domaine min
Domaine Max
Nb d'octets
float
simple
6
3.4 * 10 ^ -38
3.4 * 10 ^ 8
4
double
double
15
1.7 * 10 ^ -308
1.7 * 10 ^ 308
8
long double
suppl.
19
3.4 * 10 ^ -4932
1.1 * 10 ^ 4932
10

Mantisse représente le nombre de chiffres significatifs de la mantisse (soit le nombre de chiffres après la ).

Remarque : Les détails de l'implémentation sont indiqués dans le fichier header de base <float.h>.

retour au sommaire


2. La déclaration des variables simples

Maintenant que nous connaissons les principaux types de variables, il nous faut encore la syntaxe pour leur déclaration :

Déclaration de variables en algorithmique

<Type> <NomVar1> , <NomVar2> , ...

Déclaration de variables en C

<Type> <NomVar1> , <NomVar2> , ...

... en gros, il s'agit de la même architecture... (c'était vraiment très intéressant, merci, au revoir...) ;)

Par exemple, en algorithmique, on a :
entier n1 , n2
réel r1 , r2
caractère c1
boolléen b1

traduit en C, cela devient :
int n1 , n2 ;
float r1 ;
double r2 ;
char c1 ;
char b1 ;

Remarques

1. r1 est passé du type algorithmique réel à float en C, alors que r2 est passé de réel à double.
Cela s'explique par le fait qu'on a choisi de coder r1 sur seulement 4 octets, et r2 sur 8 octets (on aurait donc besoin de pouvoir monter plus haut en valeur avec r2 qu'avec r1...).

2. le type algorithmique "booléen" (VRAI / FAUX) n'existe pas en C, on utilise alors un type numérique (int ou char, qui se code sur moins d'octets) pour le représenter.
VRAI devient alors la valeur numérique 1, et FAUX devient 0.

3. le type algorithmique "chaines de caractères" n'existe pas non plus, on utilise des tableaux de caractères... mais on étudiera cela plus loin.

retour au sommaire

2a. Les constantes numériques

En pratique, nous utilisons souvent des valeurs constantes pour calculer, pour initialiser des variables, pour les comparer aux variables, etc... Dans ces cas, l'ordinateur doit attribuer un type numérique aux valeurs constantes. Pour pouvoir prévoir le résultat et le type exact des calculs, il est important pour le programmeur de connaître les règles selon lesquelles l'ordinateur choisit les types pour les constantes.

Les constantes entières

1. Type automatique :
Lors de l'attribution d'un type à une constante entière, C choisit en général la solution la plus économique :
Pour les constantes entières :
* Si possible, les constantes entières obtiennent le type int.
* Si le nombre est trop grand pour int (par exemple: -40000 ou +40000) il aura automatiquement le type long.
* Si le nombre est trop grand pour long, il aura le type unsigned long.
* Si le nombre est trop grand pour unsigned long, la réaction du programme est imprévisible...

2. Type forcé :
Si nous voulons forcer l'ordinateur à utiliser un type de notre choix, nous pouvons employer les suffixes suivants :
u ou U pour unsigned int ou unsigned long (exemple : 550U)
l
ou L pour long (exemple : 123456789L)
ul
ou UL pour unsigned long (exemple : 12092UL)

3. Base octale et hexadecimale
Il est possible de déclarer des constantes entières en utilisant la base octale ou hexadécimale :
* Si une constante entière commence par 0 (zéro), alors elle est interprétée en base octale.
* Si une constante entière commence par 0x ou 0X , alors elle est interprétée en base hexadécimale.

Exemple : 100 (decimal) = 0144(octal) = 0X64(hexadecimal) = 1100100(binaire)

Les constantes rationnelles

Les constantes rationnelles peuvent être indiquées:
* en notation décimale, (à l'aide d'un point décimal : 123.4)
* en notation exponentielle, (à l'aide d'un exposant séparé du nombre décimal par les caractères "e" ou "E" : 1234E-1)

1. Type automatique :
L'ordinateur reconnaît les constantes rationnelles au point décimal ou au séparateur de l'exposant ("e" ou "E").
Par défaut, les constantes rationnelles sont du type double.

2. Type forcé :
* Le suffixe f ou F force l'utilisation du type float.
* Le suffixe l ou L force l'utilisation du type long double.

Les caractères constants

Les constantes qui désignent un (seul) caractère sont toujours indiquées entre des apostrophes: par exemple "x".
La valeur d'un caractère constant est le code interne de ce caractère. Ce code (ici : le code ASCII) est dépendant de la machine. Les caractères constants peuvent apparaître dans des opérations arithmétiques ou logiques, mais en général ils sont utilisés pour être comparés à des variables.

Cas particulier : le caractère NULL
La constante "\0" qui a la valeur numérique zéro (ne pas à confondre avec le caractère "0" !!) a une signification spéciale dans le traitement et la mémorisation des chaînes de caractères: En C le caractère "\0" définit la fin d'une chaîne de caractères.

retour au sommaire

2b. Initialisation de variables

En C, il est possible d'initialiser les variables lors de leur déclaration :
int MAX=1023;
char TAB="\t";
float X=1.05e-4;

const

En utilisant l'attribut const, nous pouvons indiquer que la valeur d'une variable ne change pas au cours d'un programme :
const int MAX=767;
const double e=2.71828182845905;
const char NEWLINE="\n";

retour au sommaire


3. Les opérateurs standards

En langage algorithmique, on affecte une valeur à une variable en faisant :
<NomVariable> := <Expression>
en <NomVariable> ranger <Expression>

En C, cela devient :
<NomVariable> = <Expression>

Affectation avec des valeurs constantes

VariableLong = 141;
VariablePi := 3.1415926;
VariableNation := "L";

Affectation avec des valeurs de variables

VariableValeur := VariableX1A;
VariableLettre :=VariableCourrier;

Affectation avec des valeurs d'expressions

VariableAire := VariablePi * pow(VariableRayon,2);
VariableMoyenne := (VariableA + VariableB) / 2;
VariableUn := pow(sin(VariableX),2) + pow(cos(VariableX),2);
VariablePlusGrandDeXEtDeY := (VariableX > VariableY);
VariableTestOK := ("a" == "a")

Remarque : le test d'égalité en C se formule avec deux signes d'égalité == , l'affectation avec un seul = .

retour au sommaire

3a. Les opérateurs connus

Opérateurs arithmétiques
+ addition
- soustraction
* multiplication
/ division
% modulo

Opérateurs logiques
&& et logique (and)
|| ou logique (or)
! négation logique (not)

Opérateurs de comparaison
== égal à
!= différent de
<, <=, >, >= comparaison classique

Opérations logiques
Les résultantes des opérations de comparaison et des opérateurs logiques sont de type int.
1 correspond toujours à VRAI
0 correspond toujours à FAUX

Remarque : les opérateurs logiques considèrent toutes valeur différentes de 0 comme VRAI :
32 && 2.3 renvoit 1
!65.34 renvoit 0
0 || !(32 > 12) renvoit 0

retour au sommaire

3b. Les opérateurs particuliers du C

Les opérateurs d'affectation

En pratique, nous retrouvons souvent des affectations comme : i = i + 2.
En C, nous utiliserons plutôt la formulation plus compacte : i += 2
L'opérateur += est un opérateur d'affectation.

Pour la plupart des expressions de la forme: expr1 = (expr1) op (expr2), il existe une formulation équivalente qui utilise un opérateur d'affectation : expr1 op= expr2.

Ainsi, on a les équivalences suivantes :
+= ajouter à
-= diminuer de
*= multiplier par
/= diviser par
%= modulo

Opérateurs d'incrémentation et de décrémentation

Les affectations les plus fréquentes sont du type VariableI += 1 ou VariableI -= 1.
En C, on peut écrire, pour cela : VariableI ++ ou VariableI --.

On peut aussi incrémenter, ou décrémenter une variable et en même temps affecter sa valeur à une autre variable.
Pour cela on fait :

X = I++ passe d'abord la valeur de I à X et incrémente après
X = I-- passe d'abord la valeur de I à X et décrémente après
X = ++I incrémente d'abord et passe la valeur incrémentée à X
X = --I décrémente d'abord et passe la valeur décrémentée à X

retour au sommaire


4. Les expressions et les instructions

Expressions

La formation des expressions est définie par récurrence :
Les constantes et les variables sont des expressions.
Les expressions peuvent être combinées entre elles par des opérateurs et former ainsi des expressions plus complexes.
Les expressions peuvent contenir des appels de fonctions et elles peuvent apparaître comme paramètres dans des appels de fonctions.

Exemples :
i = 0
i++
X = pow(A,4)
printf(" Bonjour !\n")
a = (5*x+10*y)*2
(a+b) >= 100
position != limite

Instructions

Une expression comme I=0 ou I++ ou printf(...) devient une instruction, si elle est suivie d'un point-virgule.

Exemples :
i=0;
i++;
X=pow(A,4);
printf(" Bonjour !\n");
a=(5*x+10*y)*2;

Evaluation et résultats

En C toutes les expressions sont évaluées et retournent une valeur comme résultat :
(3+2==5) retourne la valeur 1 (VRAI)
A=5+3 retourne la valeur 8

Comme les affectations sont aussi interprétées comme des expressions, il est possible de profiter de la valeur rendue par l'affectation :
((A=sin(X)) == 0.5)

retour au sommaire


5. Les priorités des opérateurs

L'ordre de l'évaluation des différentes parties d'une expression correspond en principe à celle que nous connaissons des mathématiques.

Exemple :
Supposons pour l'instruction suivante: A=5, B=10, C=1 :
X = 2*A+3*B+4*C;
L'ordinateur évalue d'abord les multiplications :
2*A ==> 10 , 3*B ==> 30 , 4*C ==> 4
Ensuite, il fait l'addition des trois résultats obtenus :
10+30+4 ==> 44
A la fin, il affecte le résultat général à la variable:
X = 44

retour au sommaire


6. Les fonctions arithmétiques standard

Les fonctions suivantes sont prédéfinies dans la bibliothèque standard <math.h>. Pour pouvoir les utiliser, le programme doit contenir la ligne :
#include <math.h>

Type de données

Les arguments et les résultats des fonctions arithmétiques sont du type double.

exp(X) fonction exponentielle
log(X) logarithme naturel ln(X)
log10(X) logarithme à base 10
pow(X,Y) X exposant Y
sqrt(X) racine carrée de X
fabs(X) valeur absolue de X
floor(X) arrondir en moins
ceil(X) arrondir en plus
fmod
(X,Y) reste rationnel de X/Y (même signe que X)
sin
(X) cos(X) tan(X) sinus, cosinus, tangente de X
asin(X) acos(X) atan(X) arcsin(X), arccos(X), arctan(X)
sinh(X) cosh(X) tanh(X) sinus, cosinus, tangente hyperboliques de X

Cette liste des fonctions ne cite que les fonctions les plus courantes.
Pour la liste complète et les constantes prédéfinies voir la définition de <math.h>.

retour au sommaire


7. Les conversions de type

La grande souplesse du langage C permet de mélanger des données de différents types dans une expression.
Avant de pouvoir calculer, les données doivent être converties dans un même type.
La plupart de ces conversions se passent automatiquement, sans l'intervention du programmeur, qui doit quand même prévoir leur effet.
Parfois il est nécessaire de convertir une donnée dans un type différent de celui que choisirait la conversion automatique; dans ce cas, nous devons forcer la conversion à l'aide d'un opérateur spécial (cast).

retour au sommaire

7a. Les conversions automatiques :

Calculs et affectations

Si un opérateur a des opérandes de différents types, les valeurs des opérandes sont converties automatiquement dans un type commun.
Ces manipulations implicites convertissent en général des types plus "petits" en des types plus "larges" ; de cette façon on ne perd pas en précision.
Lors d'une affectation, la donnée à droite du signe d'égalité est convertie dans le type à gauche du signe d'égalité.
Dans ce cas, il peut y avoir perte de précision si le type de la destination est plus faible que celui de la source.

Appels de fonctions

Lors de l'appel d'une fonction, les paramètres sont automatiquement convertis dans les types déclarés dans la définition de la fonction.

Règles de conversion automatique

Conversions automatiques lors d'une opération avec...
1. deux entiers :
les types char et short sont convertis en int, ensuite, l'ordinateur choisit le plus large des deux types dans l'échelle suivant e: int, unsigned int, long, unsigned long.
2. un entier et un rationnel :
Le type entier est converti dans le type du rationnel.
3. deux rationnels :
on choisit le plus large des deux types selon l'échelle suivante: float, double, long double
4. affectations et opérateurs d'affectations :
Lors d'une affectation, le résultat est toujours converti dans le type de la destination. Si ce type est plus faible, il peut y avoir une perte de précision.

Phénomènes imprévus...

Le mélange de différents types numériques dans un calcul peut inciter à ne pas tenir compte des phénomènes de conversion et conduit parfois à des résultats imprévus... A éviter donc...

retour au sommaire

7b. Les conversions forcées :

Il est possible de convertir explicitement une valeur en un type quelconque en forçant la transformation à l'aide de la syntaxe: (<Type>) <Expression>

Exemple : VariableRationnelle = (float) VariableEntiere

retour au sommaire


8. Définition de constantes et de macros

On peut definir des constantes utiles lors du parametrage du programme que l'on vient de coder.

Définition d'une constante

#define Nom_Variable 12
main()
{
int n = Nom_Variable;
}

Le compilateur remplace limit par 12 partout dans le programme.
Il n'est pas nécessaire de mettre un ; à la fin de la définition d'une constante.

Les macros

Une macro est une fonction à paramètre qui renvoit le résultat de l'opération logique à effectuer sur ces paramètres .
Les macros à paramètres sont surtout utiles dans C, car dans C++ on peut définir du code "en ligne" qui joue presque le même rôle, mais est mieux testé par le compilateur.
Dans l'exemple ci-dessous, une macro est définie puis utilisée.

#define DoubleByte(a,b) ((a<<8) | (b&255))
int i = DoubleByte(12,200);

retour au sommaire
chapitre suivant