1. Les types simples
1a. Les types entiers
1b. Les types rationnels2. La déclaration des variables simples
2a. Les constantes numériques
2b. Initialisation de variables3. 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
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"...
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é)
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...
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
|
Où 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
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.
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.
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";
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 = .
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
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
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)
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
6. Les fonctions arithmétiques standard
Les fonctions suivantes sont prédéfinies dans la bibliothèque standard
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 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).
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...
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
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);