Historique de C++
Très tôt, les concepts de la programmation orientée objet (en abrégé P.O.O.) ont donné naissance
à de nouveaux langages dits « orientés objets » tels que Smalltalk, Simula, Eiffel ou,
plus récemment, Java. Le langage C++, quant à lui, a été conçu suivant une démarche
hybride. En effet, Bjarne Stroustrup, son créateur, a cherché à adjoindre à un langage structuré
existant (le C), un certain nombre de spécificités lui permettant d’appliquer les concepts
de P.O.O. Dans une certaine mesure, il a permis à des programmeurs C d’effectuer une transition
en douceur de la programmation structurée vers la P.O.O. De sa conception jusqu’à sa
normalisation, le langage C++ a quelque peu évolué. Initialement, un certain nombre de
publications de AT&T ont servi de référence du langage. Les dernières en date sont : la version
2.0 en 1989, les versions 2.1 et 3 en 1991. C’est cette dernière qui a servi de base au travail
du comité ANSI qui, sans la remettre en cause, l’a enrichie de quelques extensions et
surtout de composants standard originaux se présentant sous forme de fonctions et de classes
génériques qu’on désigne souvent par le sigle S.T.L (Standard Template Library). La norme
définitive de C++ a été publiée par l’ANSI en juillet 1998.
1 Présentation par l’exemple de quelques
instructions du langage C++
1.1 Un exemple de programme en langage C++
Voici un exemple de programme en langage C++, accompagné d’un exemple d’exécution.
Avant de lire les explications qui suivent, essayez d’en percevoir plus ou moins le fonctionnement.
#include <iostream>
#include <cmath>
using namespace std ;
main()
{ int i ;
float x ;
float racx ;
const int NFOIS = 5 ;
cout << "Bonjour\n" ;
cout << "Je vais vous calculer " << NFOIS << " racines carrees\n" ;
for (i=0 ; i<NFOIS ; i++)
{ cout << "Donnez un nombre : " ;
cin >> x ;
if (x < 0.0)
cout << "Le nombre " << x << "ne possede pas de racine carree\n " ;
else
{ racx = sqrt (x) ;
cout << "Le nombre " << x << " a pour racine carree : " << racx << "\n" ;
}
}
cout << "Travail termine - au revoir " ;
}
Bonjour
Je vais vous calculer 5 racines carrees
Donnez un nombre : 8
Le nombre 8 a pour racine carree : 2.82843
Donnez un nombre : 4
Le nombre 4 a pour racine carree : 2
Donnez un nombre : 0.25
Le nombre 0.25 a pour racine carree : 0.5
Donnez un nombre : 3.4
Le nombre 3.4 a pour racine carree : 1.84391
Donnez un nombre : 2
Le nombre 2 a pour racine carree : 1.41421
Travail termine - au revoir
1.3 Déclarations
Les quatre instructions :
int i ;
float x ;
float racx ;
const int NFOIS = 5 ;
sont des « déclarations ».
La première précise que la variable nommée i est de type int, c’est-à-dire qu’elle est destinée
à contenir des nombres entiers (relatifs). Nous verrons qu’en C++ il existe plusieurs types
d’entiers.
Les deux autres déclarations précisent que les variables x et racx sont de type float, c’est-àdire
qu’elles sont destinées à contenir des nombres flottants (approximation de nombres
réels). Là encore, nous verrons qu’en C++ il existe plusieurs types flottants.
Enfin, la quatrième déclaration indique que NFOIS est une constante de type entier, ayant la
valeur 5. Contrairement à une variable, la valeur d’une constante ne peut pas être modifiée.
En C++, comme dans la plupart des langages actuels, les déclarations des types des variables
sont obligatoires. Elles doivent apparaître avant d’être effectivement utilisées. Ici, nous les
avons regroupées au début du programme (on devrait plutôt dire : au début de la fonction
main). Il en ira de même pour toutes les variables définies dans une fonction ; on les appelle
« variables locales » (en toute rigueur, les variables définies dans notre exemple sont des
variables locales de la fonction main). Nous verrons également (dans le chapitre consacré
aux fonctions) qu’on peut définir des variables en dehors de toute fonction : on parlera alors
de variables globales.
1.9 L’instruction using
La norme de C++ a introduit la notion d’« espaces de noms » (namespace). Elle permet de
restreindre la « portée » des symboles à une certaine partie d’un programme et donc, en particulier,
de règler les problèmes qui peuvent se poser quand plusieurs bibliothèques utilisent
les mêmes noms. Cette notion d’espace de noms sera étudiée par la suite. Pour l’instant, retenez
que les symboles déclarés dans le fichier iostream appartiennent à l’espace de noms std.
L’instruction using sert précisément à indiquer que l’on se place « dans cet espace de noms
std » (attention, si vous placez l’instruction using avant l’incorporation des fichiers en-tête,
vous obtiendrez une erreur car vous ferez référence à un espace de noms qui n’a pas encore
été défini !).
1.10 Exemple de programme utilisant le type caractère
Voici un second exemple de programme, accompagné de deux exemples d’exécution, destiné
à vous montrer l’utilisation du type « caractère ». Il demande à l’utilisateur de choisir une
opération parmi l’addition ou la multiplication, puis de fournir deux nombres entiers ; il affiche
alors le résultat correspondant.
#include <iostream>
using namespace std ;
main()
{ char op ;
int n1, n2 ;
cout << "opération souhaitée (+ ou *) ? " ;
cin >> op ;
cout << "donnez 2 nombres entiers : " ;
cin >> n1 >> n2 ;
if (op == ’+’) cout << "leur somme est : " << n1+n2 << "\n" ;
else cout << "leur produit est : " << n1*n2 << "\n" ;
}
opération souhaitée (+ ou *) ? +
donnez 2 nombres entiers : 25 13
2.4 Le format libre
Comme tous les langages récents, le C++ autorise une mise en page parfaitement libre. En
particulier, une instruction peut s’étendre sur un nombre quelconque de lignes, et une même
ligne peut comporter autant d’instructions que vous le souhaitez. Les fins de ligne ne jouent
pas de rôle particulier, si ce n’est celui de séparateur, au même titre qu’un espace, sauf dans
les « constantes chaînes » où elles sont interdites ; de telles constantes doivent impérativement
être écrites à l’intérieur d’une seule ligne. Un identificateur ne peut être coupé en deux
par une fin de ligne, ce qui semble évident.
Bien entendu, cette liberté de mise en page possède des contreparties. Notamment, le risque
existe, si l’on n’y prend garde, d’aboutir à des programmes peu lisibles.
À titre d’exemple, voyez comment pourrait être (mal) présenté notre programme précédent :
1 La notion de type
La mémoire centrale est un ensemble de positions binaires nommées bits. Les bits sont regroupés
en octets (8 bits), et chaque octet est repéré par ce qu’on nomme son adresse.
L’ordinateur, compte tenu de sa technologie actuelle, ne sait représenter et traiter que des
informations exprimées sous forme binaire. Toute information, quelle que soit sa nature,
devra être codée sous cette forme. Dans ces conditions, on voit qu’il ne suffit pas de connaître
le contenu d’un emplacement de la mémoire (d’un ou de plusieurs octets) pour être en
mesure de lui attribuer une signification. Par exemple, si vous savez qu’un octet contient le
« motif binaire » suivant :
01001101
vous pouvez considérer que cela représente le nombre entier 77 (puisque le motif ci-dessus
correspond à la représentation en base 2 de ce nombre). Mais pourquoi cela représenterait-il
un nombre ? En effet, toutes les informations (nombres entiers, nombres réels, nombres complexes,
caractères, instructions de programme en langage machine, graphiques, images, sons,
vidéos...) devront, au bout du compte, être codées en binaire.
Dans ces conditions, les huit bits ci-dessus peuvent peut-être représenter un caractère ; dans
ce cas, si nous connaissons la convention employée sur la machine concernée pour représenter
les caractères, nous pouvons lui faire correspondre un caractère donné (par exemple M,
dans le cas du code ASCII). Ils peuvent également représenter une partie d’une instruction
machine ou d’un nombre entier codé sur 2 octets, ou d’un nombre réel codé sur 4 octets, ou...
On comprend donc qu’il n’est pas possible d’attribuer une signification à une information
binaire tant que l’on ne connaît pas la manière dont elle a été codée. Qui plus est, en général,
il ne sera même pas possible de « traiter » cette information. Par exemple, pour additionner
deux informations, il faudra savoir quel codage a été employé afin de pouvoir mettre en
oeuvre les bonnes instructions (en langage machine). Par exemple, on ne fait pas appel aux
mêmes circuits électroniques pour additionner deux nombres codés sous forme « entière » et
deux nombres codés sous forme « flottante ».
D’une manière générale, la notion de type, telle qu’elle existe dans les langages évolués, sert
à régler (entre autres choses) les problèmes que nous venons d’évoquer.
Les types de base du langage C++ se répartissent en quatre catégories en fonction de la
nature des informations qu’ils permettent de représenter :
• nombres entiers (mot-clé int) ;
• nombres flottants (mot-clé float ou double) ;
• caractères (mot-clé char) ;
• valeurs booléennes, c’est-à-dire dont la valeur est soit vrai, soit faux (mot-clé bool).
2 Les types entiers
2.1 Les différents types usuels d’entiers prévus par C++
C++ prévoit que, sur une machine donnée, on puisse trouver jusqu’à trois tailles différentes
d’entiers, désignées par les mots-clés suivants :
• short int (qu’on peut abréger en short) ;
• int (c’est celui que nous avons rencontré dans le chapitre précédent) ;
• long int (qu’on peut abréger en long).
Chaque taille impose naturellement ses limites. Toutefois, ces dernières dépendent non seulement
du mot-clé considéré, mais également de la machine utilisée : tous les int n’ont pas la
même taille sur toutes les machines ! Fréquemment, deux des trois mots-clés correspondent à
une même taille1.
2.2 Leur représentation en mémoire
Pour fixer les idées, nous raisonnerons ici sur des nombres entiers représentés sur 16 bits ,
mais il sera facile de généraliser notre propos à une taille quelconque.
Quelle que soit la machine (et donc, a fortiori, le langage !), les entiers sont codés en utilisant
un bit pour représenter le signe (0 pour positif et 1 pour négatif).
a) Lorsqu’il s’agit d’un nombre positif (ou nul), sa valeur absolue est écrite en base 2, à la
suite du bit de signe. Voici quelques exemples de codages de nombres (à gauche, le nombre
en décimal, au centre, le codage binaire correspondant, à droite, le même codage exprimé en
hexadécimal) :
1 0000000000000001 0001
2 0000000000000010 0002
3 0000000000000011 0003
16 0000000000010000 0010
127 0000000001111111 007F
255 0000000011111111 00FF
b) Lorsqu’il s’agit d’un nombre négatif, sa valeur absolue est codée généralement suivant ce
que l’on nomme la « technique du complément à deux »2. Pour ce faire, cette valeur est d’abord
exprimée en base 2 puis tous les bits sont inversés (1 devient 0 et 0 devient 1) et, enfin, on
ajoute une unité au résultat. Voici quelques exemples (avec la même présentation que
précédemment) :
-1 1111111111111111 FFFF
-2 1111111111111110 FFFE
-3 1111111111111101 FFFD
-4 1111111111111100 FFFC
-16 1111111111110000 FFF0
-256 1111111100000000 FF00
1. Dans une implémentation donnée, on peut connaître les caractéristiques des différents types entiers grâce à des
constantes (telles que INT_MAX, INT_MIN) définies dans le fichier en-tête climits.
2. Bien que non imposée totalement par la norme, cette technique tend à devenir universelle. Dans les
(anciennes)*
mineures (deux représentations du zéro : +0 et -0, différence d’une unité sur la plage des valeurs couvertes pour une
Remarques
1 Le nombre 0 est codé d’une seule manière (0000000000000000).
2 Si l’on ajoute 1 au plus grand nombre positif (ici 0111111111111111, soit 7FFF en
hexadécimal ou 32768 en décimal) et que l’on ne tient pas compte de la dernière retenue
(ou, ce qui revient au même, si l’on ne considère que les 16 derniers bits du résultat),
on obtient... le plus petit nombre négatif possible (ici 1000000000000000, soit
8000 en hexadécimal ou -32768 en décimal). Nous verrons qu’en C++, la situation dite
« de dépassement de capacité » (correspondant au cas où un résultat d’opération
s’avère trop grand pour le type prévu) sera traité ainsi, en ignorant un bit de retenue...
taille d’entier donnée).
2.3 Les types entiers non signés
De façon quelque peu atypique, C++ vous autorise à définir trois autres types voisins des précédents
en utilisant le qualificatif unsigned. Dans ce cas, on ne représente plus que des nombres
positifs pour lesquels aucun bit de signe n’est nécessaire. Cela permet théoriquement de
doubler la taille des nombres représentables ; par exemple, avec 16 bits, on passe de l’intervalle
[-32768; 32767] à l’intervalle [0 ; 65535]. Mais cet avantage est bien dérisoire, par rapport
aux risques que comporte l’utilisation de ces types (songez qu’une simple expression
telle que n-p va poser problème dès que la valeur de p sera supérieure à celle de n !).
En pratique, ces types non signés seront réservés à la manipulation directe d’un « motif
binaire » (tel un « mot d’état ») et non pas pour faire des calculs. Nous verrons d’ailleurs
qu’il existe des opérateurs spécialisés dits « de manipulation de bits ». Comme nous aurons
l’occasion de le rappeler, il est conseillé d’éviter de méler des entiers signés et des entiers
2.4 Notation des constantes entières
La façon la plus naturelle d’introduire une constante entière dans un programme est de
l’écrire simplement sous forme décimale, avec ou sans signe, comme dans ces exemples :
+533 48 -273
Vous pouvez également utiliser une notation octale (base 8) ou hexadécimale (base 16). La
forme octale se note en faisant précéder le nombre écrit en base 8 du chiffre 0.
Par exemple :
014 correspond à la valeur décimale 12,
037 correspond à la valeur décimale 31.
La forme hexadécimale se note en faisant précéder le nombre écrit en hexadécimal (les dix
premiers chiffres se notent 0 à 9, A correspond à dix, B à onze... F à quinze) des deux caractères
0x (ou 0X). Par exemple :
0x1A correspond à la valeur décimale 26 (16+10)
non signés dans une même expression, même si cela est théoriquement autorisé par la norme.
Les deux dernières notations doivent cependant être réservées aux situations dans lesquelles
on s’intéresse plus au motif binaire qu’à la valeur numérique de la constante en question.
D’ailleurs, ces constantes sont de type non signé (alors que les constantes écrites en notation
décimale sont bien signées).
Informations complémentaires
Par défaut, une constante entière écrite en notation décimale est codée dans l’un des deux
types signé int ou long (on utilise le type le plus petit, suffisant pour la représenter). On
peut imposer à une constante décimale
- d’être non signée, en la suffixant par « u », comme dans : 1u ou -25u ;
- d’être du type long, en la suffixant par « l », comme dans 456l ;
- d’être du type unsigned long en la suffixant par « ul », comme dans 2649ul.
Là encore, ces
3 Les types flottants
3.1 Les différents types et leur représentation en mémoire
Les types flottants permettent de représenter, de manière approchée, une partie des nombres
réels. Pour ce faire, ils s’inspirent de la notation scientifique (ou exponentielle) bien connue
qui consiste à écrire un nombre sous la forme 1.5 1022 ou 0.472 10-8 ; dans une telle notation,
on nomme « mantisses » les quantités telles que 1.5 ou 0.472 et « exposants » les quantités
telles que 22 ou -8.
Plus précisément, un nombre réel sera représenté en flottant, en déterminant deux quantités
M (mantisse) et E (exposant) telles que la valeur M . B E
représente une approximation de ce nombre. La base B est généralement unique pour une
machine donnée (il s’agit souvent de 2 ou de 16) et elle ne figure pas explicitement dans la
représentation machine du nombre.
C++ prévoit trois types de flottants correspondant à des tailles différentes : float, double et
long double.
La connaissance des caractéristiques exactes du système de codage n’est généralement pas
indispensable, sauf lorsque l’on doit faire une analyse fine des erreurs de calcul1. En revan-
che, il est important de noter que de telles représentations sont caractérisées par deux
éléments :
• La précision : lors du codage d’un nombre décimal quelconque dans un type flottant, il est
nécessaire de ne conserver qu’un nombre fini de bits. Or la plupart des nombres s’exprimant
avec un nombre limité de décimales ne peuvent pas s’exprimer de façon exacte dans un tel
codage. On est donc obligé de se limiter à une représentation approchée en faisant ce que
l’on nomme une erreur de troncature. Quelle que soit la machine utilisée, on est assuré que
cette erreur (relative) ne dépassera pas 10-6 pour le type float et 10-10 pour le type long double.
• Le domaine couvert, c’est-à-dire l’ensemble des nombres représentables à l’erreur de troncature
près. Là encore, quelle que soit la machine utilisée, on est assuré qu’il s’étendra au
moins de 10-37 à 10+37.
4 Les opérateurs relationnels
Comme tout langage, C++ permet de comparer des expressions à l’aide d’opérateurs classiques
de comparaison. En voici un exemple :
2 * a > b + 5
Le résultat de la comparaison est une valeur booléenne prenant l’une des deux valeurs true ou
false.
Les expressions comparées pourront être d’un type de base quelconque et elles seront soumises
aux règles de conversion présentées dans le paragraphe précédent. Cela signifie qu’au bout
du compte on ne sera amené à comparer que des expressions de type numérique, même si
dans les opérandes figurent des valeurs de type short, char ou bool (true>false sera vraie).
Voici la liste des opérateurs relationnels existant en C++ :
Opérateur Signification
< inférieur à
<= inférieur ou égal à
> supérieur à
>= supérieur ou égal à
== égal à
!= différent de
Remarquez bien la notation (==) de l’opérateur d’égalité, le signe = étant réservé aux affectations.
En ce qui concerne leur priorité, il faut savoir que les quatre premiers opérateurs (<, <=, > et
>=) sont de même priorité. Les deux derniers (== et !=) possèdent également la même priorité,
mais celle-ci est inférieure à celle des précédents. Ainsi, l’expression :
a < b == c < d
est interprétée comme :
( a < b) == (c < d)
ce qui, en C++, a effectivement une signification, étant donné que les expressions a<b et c<d
sont, finalement, des quantités entières. En fait, cette expression prendra la valeur 1 lorsque
les relations a < b et c < d auront toutes les deux la même valeur, c’est-à-dire soit lorsqu’elles
seront toutes les deux vraies, soit lorsqu’elles seront toutes les deux fausses. Elle prendra la
valeur 0 dans le cas contraire.
D’autre part, ces opérateurs relationnels sont moins prioritaires que les opérateurs arithmétiques.
Cela permet souvent d’éviter certaines parenthèses dans des expressions.
11 L’opérateur conditionnel
Considérons l’instruction suivante :
if ( a>b )
max = a ;
else
max = b ;
Elle attribue à la variable max la plus grande des deux valeurs de a et de b. La valeur de max
pourrait être définie par cette phrase :
Si a>b alors a sinon b
En C++, il est possible, grâce à l’aide de l’opérateur conditionnel, de traduire presque littéralement
la phrase ci-dessus de la manière suivante :
max = a>b ? a : b
L’expression figurant à droite de l’opérateur d’affectation est en fait constituée de trois
expressions (a>b, a et b) qui sont les trois opérandes de l’opérateur conditionnel, lequel se
matérialise par deux symboles séparés : ? et :.
D’une manière générale, cet opérateur évalue la première expression qui joue le rôle d’une
condition. Comme toujours en C++, celle-ci peut être en fait de n’importe quel type. Si sa
valeur est différente de zéro, il y a évaluation du second opérande, ce qui fournit le résultat ;
si sa valeur est nulle, en revanche, il y a évaluation du troisième opérande, ce qui fournit le
résultat.
Voici un autre exemple d’une expression calculant la valeur absolue de 3*a + 1 :
3*a+1 > 0 ? 3*a+1 : -3*a-1
L’opérateur conditionnel dispose d’une faible priorité (il arrive juste avant l’affectation), de
sorte qu’il est rarement nécessaire d’employer des parenthèses pour en délimiter les différents
opérandes (bien que cela puisse parfois améliorer la lisibilité du programme). Voici,
toutefois, un cas où les parenthèses sont indispensables :
z = (x=y) ? a : b
Le calcul de cette expression amène tout d’abord à affecter la valeur de y à x. Puis, si cette
valeur est non nulle, on affecte la valeur de a à z. Si, au contraire, cette valeur est nulle, on
affecte la valeur de b à z.
Il est clair que cette expression est différente de :
z = x = y ? a : b
laquelle serait évaluée comme :
z = x = ( y ? a : b )
Bien entendu, une expression conditionnelle peut, comme toute expression, apparaître à son
tour dans une expression plus complexe. Voici, par exemple, une instruction (notez qu’il
s’agit effectivement d’une instruction, car elle se termine par un point-virgule) affectant à z la
plus grande des valeurs de a et de b :
z = ( a>b ? a : b ) ;
De même, rien n’empêche que l’expression conditionnelle soit évaluée sans que sa valeur*
soit utilisée comme dans cette instruction :
a>b ? i++ : i-- ;
Ici, suivant que la condition a>b est vraie ou fausse, on incrémentera ou on décrémentera la
variable i.
13 L’opérateur sizeof
L’opérateur sizeof, dont l’emploi ressemble à celui d’une fonction, fournit la taille en octets
(n’oubliez pas que l’octet est, en fait, la plus petite partie adressable de la mémoire). Par
exemple, dans une implémentation où le type int est représenté sur 2 octets et le type double
sur 8 octets, si l’on suppose que l’on a affaire à ces déclarations :
int n ;
double z ;
• l’expression sizeof(n) vaudra 2 ;
• l’expression sizeof(z) vaudra 8.
Cet opérateur peut également s’appliquer à un type de nom donné. Ainsi, dans l’implémentation
précédemment citée :
• sizeof(int) vaudra 2 ;
• sizeof(double) vaudra 8.
Quelle que soit l’implémentation, sizeof(char) vaudra toujours 1 (par définition, en quelque
sorte).
Cet opérateur offre un intérêt :
• lorsque l’on souhaite écrire des programmes portables dans lesquels il est nécessaire de connaître
la taille exacte de certains éléments ;
• pour éviter d’avoir à calculer soi-même la taille d’objets d’un type relativement complexe
pour lequel on n’est pas certain de la manière dont il sera implémenté par le compilateur. Ce
sera notamment le cas des structures ou des objets.
3 L’instruction switch
3.1 Exemples d’introduction de l’instruction switch
a) Premier exemple
Voyez ce premier exemple de programme accompagné de trois exemples d’exécution.
#include <iostream>
using namespace std ;
main()
{ int n ;
cout << "donnez un entier : " ;
cin >> n ;
switch (n)
{ case 0 : cout << "nul\n" ;
break ;
case 1 : cout << "un\n" ;
break ;
case 2 : cout << "deux\n" ;
break ;
}
cout << "au revoir\n" ;
}
donnez un entier : 0
nul
au revoir
donnez un entier : 2
deux
au revoir
donnez un entier : 5
au revoir
2.3 Imbrication des instructions if
Nous avons déjà mentionné que les instructions figurant dans chaque partie du choix d’une
instruction pouvaient être absolument quelconques. En particulier, elles peuvent, à leur tour,
renfermer d’autres instructions if. Or, compte tenu de ce que cette instruction peut comporter
ou ne pas comporter de else, il existe certaines situations où une ambiguïté apparaît. C’est le
cas dans cet exemple :
if (a<=b) if (b<=c) cout << "ordonné" ;
else cout << "non ordonné" ;
Est-il interprété comme le suggère cette présentation ?
if (a<=b) if (b<=c) cout << "ordonné" ;
else cout << "non ordonné" ;
ou bien comme le suggère celle-ci ?
if (a<=b) if (b<=c) cout << "ordonné" ;
else cout << "non ordonné" ;
La première interprétation conduirait à afficher "non ordonné" lorsque la condition a<=b est
fausse, tandis que la seconde n’afficherait rien dans ce cas. La règle adoptée par le langage
C++ pour lever une telle ambiguïté est la suivante :
Dans notre exemple, c’est la seconde présentation qui suggère le mieux ce qui se passe.
Voici un exemple d’utilisation de if imbriqués. Il s’agit d’un programme de facturation avec
remise. Il lit en donnée un simple prix hors taxes et calcule le prix TTC correspondant (avec
un taux de TVA constant de 19,6 %). Il établit ensuite une remise dont le taux dépend de la
valeur ainsi obtenue, à savoir :
• 0 % pour un montant inférieur à 1 000 euros ;
Un else se rapporte toujours au dernier if rencontré auquel un else n’a pas encore
été attribué.
Les instructions de contrôle
• 1 % pour un montant supérieur ou égal à 1 000 euros et inférieur à 2 000 euros ;
• 3 % pour un montant supérieur ou égal à 2 000 euros et inférieur à 5 000 euros ;
• 5 % pour un montant supérieur ou égal à 5 000 euros.
#include <iostream>
using namespace std ;
main()
{ const double TAUX_TVA = 19.6 ;
double ht, ttc, net, tauxr, remise ;
cout << "donnez le prix hors taxes : " ;
cin >> ht ;
ttc = ht * ( 1. + TAUX_TVA/100.) ;
if ( ttc < 1000.) tauxr = 0 ;
else if ( ttc < 2000 ) tauxr = 1. ;
else if ( ttc < 5000 ) tauxr = 3. ;
else tauxr = 5. ;
remise = ttc * tauxr / 10
0. ;
net = ttc - remise ;
cout << "prix ttc = " << ttc << "\n" ;
cout << "remise = " << remise << "\n" ;
cout << "net à payer = " << net << "\n" ;
}
donnez le prix hors taxes : 500
prix ttc = 598
remise = 0
net à payer = 598
donnez le prix hors taxes : 4000
prix ttc = 4784
remise = 143.52
net à payer = 4640.48
Très tôt, les concepts de la programmation orientée objet (en abrégé P.O.O.) ont donné naissance
à de nouveaux langages dits « orientés objets » tels que Smalltalk, Simula, Eiffel ou,
plus récemment, Java. Le langage C++, quant à lui, a été conçu suivant une démarche
hybride. En effet, Bjarne Stroustrup, son créateur, a cherché à adjoindre à un langage structuré
existant (le C), un certain nombre de spécificités lui permettant d’appliquer les concepts
de P.O.O. Dans une certaine mesure, il a permis à des programmeurs C d’effectuer une transition
en douceur de la programmation structurée vers la P.O.O. De sa conception jusqu’à sa
normalisation, le langage C++ a quelque peu évolué. Initialement, un certain nombre de
publications de AT&T ont servi de référence du langage. Les dernières en date sont : la version
2.0 en 1989, les versions 2.1 et 3 en 1991. C’est cette dernière qui a servi de base au travail
du comité ANSI qui, sans la remettre en cause, l’a enrichie de quelques extensions et
surtout de composants standard originaux se présentant sous forme de fonctions et de classes
génériques qu’on désigne souvent par le sigle S.T.L (Standard Template Library). La norme
définitive de C++ a été publiée par l’ANSI en juillet 1998.
1 Présentation par l’exemple de quelques
instructions du langage C++
1.1 Un exemple de programme en langage C++
Voici un exemple de programme en langage C++, accompagné d’un exemple d’exécution.
Avant de lire les explications qui suivent, essayez d’en percevoir plus ou moins le fonctionnement.
#include <iostream>
#include <cmath>
using namespace std ;
main()
{ int i ;
float x ;
float racx ;
const int NFOIS = 5 ;
cout << "Bonjour\n" ;
cout << "Je vais vous calculer " << NFOIS << " racines carrees\n" ;
for (i=0 ; i<NFOIS ; i++)
{ cout << "Donnez un nombre : " ;
cin >> x ;
if (x < 0.0)
cout << "Le nombre " << x << "ne possede pas de racine carree\n " ;
else
{ racx = sqrt (x) ;
cout << "Le nombre " << x << " a pour racine carree : " << racx << "\n" ;
}
}
cout << "Travail termine - au revoir " ;
}
Bonjour
Je vais vous calculer 5 racines carrees
Donnez un nombre : 8
Le nombre 8 a pour racine carree : 2.82843
Donnez un nombre : 4
Le nombre 4 a pour racine carree : 2
Donnez un nombre : 0.25
Le nombre 0.25 a pour racine carree : 0.5
Donnez un nombre : 3.4
Le nombre 3.4 a pour racine carree : 1.84391
Donnez un nombre : 2
Le nombre 2 a pour racine carree : 1.41421
Travail termine - au revoir
1.3 Déclarations
Les quatre instructions :
int i ;
float x ;
float racx ;
const int NFOIS = 5 ;
sont des « déclarations ».
La première précise que la variable nommée i est de type int, c’est-à-dire qu’elle est destinée
à contenir des nombres entiers (relatifs). Nous verrons qu’en C++ il existe plusieurs types
d’entiers.
Les deux autres déclarations précisent que les variables x et racx sont de type float, c’est-àdire
qu’elles sont destinées à contenir des nombres flottants (approximation de nombres
réels). Là encore, nous verrons qu’en C++ il existe plusieurs types flottants.
Enfin, la quatrième déclaration indique que NFOIS est une constante de type entier, ayant la
valeur 5. Contrairement à une variable, la valeur d’une constante ne peut pas être modifiée.
En C++, comme dans la plupart des langages actuels, les déclarations des types des variables
sont obligatoires. Elles doivent apparaître avant d’être effectivement utilisées. Ici, nous les
avons regroupées au début du programme (on devrait plutôt dire : au début de la fonction
main). Il en ira de même pour toutes les variables définies dans une fonction ; on les appelle
« variables locales » (en toute rigueur, les variables définies dans notre exemple sont des
variables locales de la fonction main). Nous verrons également (dans le chapitre consacré
aux fonctions) qu’on peut définir des variables en dehors de toute fonction : on parlera alors
de variables globales.
1.9 L’instruction using
La norme de C++ a introduit la notion d’« espaces de noms » (namespace). Elle permet de
restreindre la « portée » des symboles à une certaine partie d’un programme et donc, en particulier,
de règler les problèmes qui peuvent se poser quand plusieurs bibliothèques utilisent
les mêmes noms. Cette notion d’espace de noms sera étudiée par la suite. Pour l’instant, retenez
que les symboles déclarés dans le fichier iostream appartiennent à l’espace de noms std.
L’instruction using sert précisément à indiquer que l’on se place « dans cet espace de noms
std » (attention, si vous placez l’instruction using avant l’incorporation des fichiers en-tête,
vous obtiendrez une erreur car vous ferez référence à un espace de noms qui n’a pas encore
été défini !).
1.10 Exemple de programme utilisant le type caractère
Voici un second exemple de programme, accompagné de deux exemples d’exécution, destiné
à vous montrer l’utilisation du type « caractère ». Il demande à l’utilisateur de choisir une
opération parmi l’addition ou la multiplication, puis de fournir deux nombres entiers ; il affiche
alors le résultat correspondant.
#include <iostream>
using namespace std ;
main()
{ char op ;
int n1, n2 ;
cout << "opération souhaitée (+ ou *) ? " ;
cin >> op ;
cout << "donnez 2 nombres entiers : " ;
cin >> n1 >> n2 ;
if (op == ’+’) cout << "leur somme est : " << n1+n2 << "\n" ;
else cout << "leur produit est : " << n1*n2 << "\n" ;
}
opération souhaitée (+ ou *) ? +
donnez 2 nombres entiers : 25 13
2.4 Le format libre
Comme tous les langages récents, le C++ autorise une mise en page parfaitement libre. En
particulier, une instruction peut s’étendre sur un nombre quelconque de lignes, et une même
ligne peut comporter autant d’instructions que vous le souhaitez. Les fins de ligne ne jouent
pas de rôle particulier, si ce n’est celui de séparateur, au même titre qu’un espace, sauf dans
les « constantes chaînes » où elles sont interdites ; de telles constantes doivent impérativement
être écrites à l’intérieur d’une seule ligne. Un identificateur ne peut être coupé en deux
par une fin de ligne, ce qui semble évident.
Bien entendu, cette liberté de mise en page possède des contreparties. Notamment, le risque
existe, si l’on n’y prend garde, d’aboutir à des programmes peu lisibles.
À titre d’exemple, voyez comment pourrait être (mal) présenté notre programme précédent :
1 La notion de type
La mémoire centrale est un ensemble de positions binaires nommées bits. Les bits sont regroupés
en octets (8 bits), et chaque octet est repéré par ce qu’on nomme son adresse.
L’ordinateur, compte tenu de sa technologie actuelle, ne sait représenter et traiter que des
informations exprimées sous forme binaire. Toute information, quelle que soit sa nature,
devra être codée sous cette forme. Dans ces conditions, on voit qu’il ne suffit pas de connaître
le contenu d’un emplacement de la mémoire (d’un ou de plusieurs octets) pour être en
mesure de lui attribuer une signification. Par exemple, si vous savez qu’un octet contient le
« motif binaire » suivant :
01001101
vous pouvez considérer que cela représente le nombre entier 77 (puisque le motif ci-dessus
correspond à la représentation en base 2 de ce nombre). Mais pourquoi cela représenterait-il
un nombre ? En effet, toutes les informations (nombres entiers, nombres réels, nombres complexes,
caractères, instructions de programme en langage machine, graphiques, images, sons,
vidéos...) devront, au bout du compte, être codées en binaire.
Dans ces conditions, les huit bits ci-dessus peuvent peut-être représenter un caractère ; dans
ce cas, si nous connaissons la convention employée sur la machine concernée pour représenter
les caractères, nous pouvons lui faire correspondre un caractère donné (par exemple M,
dans le cas du code ASCII). Ils peuvent également représenter une partie d’une instruction
machine ou d’un nombre entier codé sur 2 octets, ou d’un nombre réel codé sur 4 octets, ou...
On comprend donc qu’il n’est pas possible d’attribuer une signification à une information
binaire tant que l’on ne connaît pas la manière dont elle a été codée. Qui plus est, en général,
il ne sera même pas possible de « traiter » cette information. Par exemple, pour additionner
deux informations, il faudra savoir quel codage a été employé afin de pouvoir mettre en
oeuvre les bonnes instructions (en langage machine). Par exemple, on ne fait pas appel aux
mêmes circuits électroniques pour additionner deux nombres codés sous forme « entière » et
deux nombres codés sous forme « flottante ».
D’une manière générale, la notion de type, telle qu’elle existe dans les langages évolués, sert
à régler (entre autres choses) les problèmes que nous venons d’évoquer.
Les types de base du langage C++ se répartissent en quatre catégories en fonction de la
nature des informations qu’ils permettent de représenter :
• nombres entiers (mot-clé int) ;
• nombres flottants (mot-clé float ou double) ;
• caractères (mot-clé char) ;
• valeurs booléennes, c’est-à-dire dont la valeur est soit vrai, soit faux (mot-clé bool).
2 Les types entiers
2.1 Les différents types usuels d’entiers prévus par C++
C++ prévoit que, sur une machine donnée, on puisse trouver jusqu’à trois tailles différentes
d’entiers, désignées par les mots-clés suivants :
• short int (qu’on peut abréger en short) ;
• int (c’est celui que nous avons rencontré dans le chapitre précédent) ;
• long int (qu’on peut abréger en long).
Chaque taille impose naturellement ses limites. Toutefois, ces dernières dépendent non seulement
du mot-clé considéré, mais également de la machine utilisée : tous les int n’ont pas la
même taille sur toutes les machines ! Fréquemment, deux des trois mots-clés correspondent à
une même taille1.
2.2 Leur représentation en mémoire
Pour fixer les idées, nous raisonnerons ici sur des nombres entiers représentés sur 16 bits ,
mais il sera facile de généraliser notre propos à une taille quelconque.
Quelle que soit la machine (et donc, a fortiori, le langage !), les entiers sont codés en utilisant
un bit pour représenter le signe (0 pour positif et 1 pour négatif).
a) Lorsqu’il s’agit d’un nombre positif (ou nul), sa valeur absolue est écrite en base 2, à la
suite du bit de signe. Voici quelques exemples de codages de nombres (à gauche, le nombre
en décimal, au centre, le codage binaire correspondant, à droite, le même codage exprimé en
hexadécimal) :
1 0000000000000001 0001
2 0000000000000010 0002
3 0000000000000011 0003
16 0000000000010000 0010
127 0000000001111111 007F
255 0000000011111111 00FF
b) Lorsqu’il s’agit d’un nombre négatif, sa valeur absolue est codée généralement suivant ce
que l’on nomme la « technique du complément à deux »2. Pour ce faire, cette valeur est d’abord
exprimée en base 2 puis tous les bits sont inversés (1 devient 0 et 0 devient 1) et, enfin, on
ajoute une unité au résultat. Voici quelques exemples (avec la même présentation que
précédemment) :
-1 1111111111111111 FFFF
-2 1111111111111110 FFFE
-3 1111111111111101 FFFD
-4 1111111111111100 FFFC
-16 1111111111110000 FFF0
-256 1111111100000000 FF00
1. Dans une implémentation donnée, on peut connaître les caractéristiques des différents types entiers grâce à des
constantes (telles que INT_MAX, INT_MIN) définies dans le fichier en-tête climits.
2. Bien que non imposée totalement par la norme, cette technique tend à devenir universelle. Dans les
(anciennes)*
mineures (deux représentations du zéro : +0 et -0, différence d’une unité sur la plage des valeurs couvertes pour une
Remarques
1 Le nombre 0 est codé d’une seule manière (0000000000000000).
2 Si l’on ajoute 1 au plus grand nombre positif (ici 0111111111111111, soit 7FFF en
hexadécimal ou 32768 en décimal) et que l’on ne tient pas compte de la dernière retenue
(ou, ce qui revient au même, si l’on ne considère que les 16 derniers bits du résultat),
on obtient... le plus petit nombre négatif possible (ici 1000000000000000, soit
8000 en hexadécimal ou -32768 en décimal). Nous verrons qu’en C++, la situation dite
« de dépassement de capacité » (correspondant au cas où un résultat d’opération
s’avère trop grand pour le type prévu) sera traité ainsi, en ignorant un bit de retenue...
taille d’entier donnée).
2.3 Les types entiers non signés
De façon quelque peu atypique, C++ vous autorise à définir trois autres types voisins des précédents
en utilisant le qualificatif unsigned. Dans ce cas, on ne représente plus que des nombres
positifs pour lesquels aucun bit de signe n’est nécessaire. Cela permet théoriquement de
doubler la taille des nombres représentables ; par exemple, avec 16 bits, on passe de l’intervalle
[-32768; 32767] à l’intervalle [0 ; 65535]. Mais cet avantage est bien dérisoire, par rapport
aux risques que comporte l’utilisation de ces types (songez qu’une simple expression
telle que n-p va poser problème dès que la valeur de p sera supérieure à celle de n !).
En pratique, ces types non signés seront réservés à la manipulation directe d’un « motif
binaire » (tel un « mot d’état ») et non pas pour faire des calculs. Nous verrons d’ailleurs
qu’il existe des opérateurs spécialisés dits « de manipulation de bits ». Comme nous aurons
l’occasion de le rappeler, il est conseillé d’éviter de méler des entiers signés et des entiers
2.4 Notation des constantes entières
La façon la plus naturelle d’introduire une constante entière dans un programme est de
l’écrire simplement sous forme décimale, avec ou sans signe, comme dans ces exemples :
+533 48 -273
Vous pouvez également utiliser une notation octale (base 8) ou hexadécimale (base 16). La
forme octale se note en faisant précéder le nombre écrit en base 8 du chiffre 0.
Par exemple :
014 correspond à la valeur décimale 12,
037 correspond à la valeur décimale 31.
La forme hexadécimale se note en faisant précéder le nombre écrit en hexadécimal (les dix
premiers chiffres se notent 0 à 9, A correspond à dix, B à onze... F à quinze) des deux caractères
0x (ou 0X). Par exemple :
0x1A correspond à la valeur décimale 26 (16+10)
non signés dans une même expression, même si cela est théoriquement autorisé par la norme.
Les deux dernières notations doivent cependant être réservées aux situations dans lesquelles
on s’intéresse plus au motif binaire qu’à la valeur numérique de la constante en question.
D’ailleurs, ces constantes sont de type non signé (alors que les constantes écrites en notation
décimale sont bien signées).
Informations complémentaires
Par défaut, une constante entière écrite en notation décimale est codée dans l’un des deux
types signé int ou long (on utilise le type le plus petit, suffisant pour la représenter). On
peut imposer à une constante décimale
- d’être non signée, en la suffixant par « u », comme dans : 1u ou -25u ;
- d’être du type long, en la suffixant par « l », comme dans 456l ;
- d’être du type unsigned long en la suffixant par « ul », comme dans 2649ul.
Là encore, ces
3 Les types flottants
3.1 Les différents types et leur représentation en mémoire
Les types flottants permettent de représenter, de manière approchée, une partie des nombres
réels. Pour ce faire, ils s’inspirent de la notation scientifique (ou exponentielle) bien connue
qui consiste à écrire un nombre sous la forme 1.5 1022 ou 0.472 10-8 ; dans une telle notation,
on nomme « mantisses » les quantités telles que 1.5 ou 0.472 et « exposants » les quantités
telles que 22 ou -8.
Plus précisément, un nombre réel sera représenté en flottant, en déterminant deux quantités
M (mantisse) et E (exposant) telles que la valeur M . B E
représente une approximation de ce nombre. La base B est généralement unique pour une
machine donnée (il s’agit souvent de 2 ou de 16) et elle ne figure pas explicitement dans la
représentation machine du nombre.
C++ prévoit trois types de flottants correspondant à des tailles différentes : float, double et
long double.
La connaissance des caractéristiques exactes du système de codage n’est généralement pas
indispensable, sauf lorsque l’on doit faire une analyse fine des erreurs de calcul1. En revan-
che, il est important de noter que de telles représentations sont caractérisées par deux
éléments :
• La précision : lors du codage d’un nombre décimal quelconque dans un type flottant, il est
nécessaire de ne conserver qu’un nombre fini de bits. Or la plupart des nombres s’exprimant
avec un nombre limité de décimales ne peuvent pas s’exprimer de façon exacte dans un tel
codage. On est donc obligé de se limiter à une représentation approchée en faisant ce que
l’on nomme une erreur de troncature. Quelle que soit la machine utilisée, on est assuré que
cette erreur (relative) ne dépassera pas 10-6 pour le type float et 10-10 pour le type long double.
• Le domaine couvert, c’est-à-dire l’ensemble des nombres représentables à l’erreur de troncature
près. Là encore, quelle que soit la machine utilisée, on est assuré qu’il s’étendra au
moins de 10-37 à 10+37.
4 Les opérateurs relationnels
Comme tout langage, C++ permet de comparer des expressions à l’aide d’opérateurs classiques
de comparaison. En voici un exemple :
2 * a > b + 5
Le résultat de la comparaison est une valeur booléenne prenant l’une des deux valeurs true ou
false.
Les expressions comparées pourront être d’un type de base quelconque et elles seront soumises
aux règles de conversion présentées dans le paragraphe précédent. Cela signifie qu’au bout
du compte on ne sera amené à comparer que des expressions de type numérique, même si
dans les opérandes figurent des valeurs de type short, char ou bool (true>false sera vraie).
Voici la liste des opérateurs relationnels existant en C++ :
Opérateur Signification
< inférieur à
<= inférieur ou égal à
> supérieur à
>= supérieur ou égal à
== égal à
!= différent de
Remarquez bien la notation (==) de l’opérateur d’égalité, le signe = étant réservé aux affectations.
En ce qui concerne leur priorité, il faut savoir que les quatre premiers opérateurs (<, <=, > et
>=) sont de même priorité. Les deux derniers (== et !=) possèdent également la même priorité,
mais celle-ci est inférieure à celle des précédents. Ainsi, l’expression :
a < b == c < d
est interprétée comme :
( a < b) == (c < d)
ce qui, en C++, a effectivement une signification, étant donné que les expressions a<b et c<d
sont, finalement, des quantités entières. En fait, cette expression prendra la valeur 1 lorsque
les relations a < b et c < d auront toutes les deux la même valeur, c’est-à-dire soit lorsqu’elles
seront toutes les deux vraies, soit lorsqu’elles seront toutes les deux fausses. Elle prendra la
valeur 0 dans le cas contraire.
D’autre part, ces opérateurs relationnels sont moins prioritaires que les opérateurs arithmétiques.
Cela permet souvent d’éviter certaines parenthèses dans des expressions.
11 L’opérateur conditionnel
Considérons l’instruction suivante :
if ( a>b )
max = a ;
else
max = b ;
Elle attribue à la variable max la plus grande des deux valeurs de a et de b. La valeur de max
pourrait être définie par cette phrase :
Si a>b alors a sinon b
En C++, il est possible, grâce à l’aide de l’opérateur conditionnel, de traduire presque littéralement
la phrase ci-dessus de la manière suivante :
max = a>b ? a : b
L’expression figurant à droite de l’opérateur d’affectation est en fait constituée de trois
expressions (a>b, a et b) qui sont les trois opérandes de l’opérateur conditionnel, lequel se
matérialise par deux symboles séparés : ? et :.
D’une manière générale, cet opérateur évalue la première expression qui joue le rôle d’une
condition. Comme toujours en C++, celle-ci peut être en fait de n’importe quel type. Si sa
valeur est différente de zéro, il y a évaluation du second opérande, ce qui fournit le résultat ;
si sa valeur est nulle, en revanche, il y a évaluation du troisième opérande, ce qui fournit le
résultat.
Voici un autre exemple d’une expression calculant la valeur absolue de 3*a + 1 :
3*a+1 > 0 ? 3*a+1 : -3*a-1
L’opérateur conditionnel dispose d’une faible priorité (il arrive juste avant l’affectation), de
sorte qu’il est rarement nécessaire d’employer des parenthèses pour en délimiter les différents
opérandes (bien que cela puisse parfois améliorer la lisibilité du programme). Voici,
toutefois, un cas où les parenthèses sont indispensables :
z = (x=y) ? a : b
Le calcul de cette expression amène tout d’abord à affecter la valeur de y à x. Puis, si cette
valeur est non nulle, on affecte la valeur de a à z. Si, au contraire, cette valeur est nulle, on
affecte la valeur de b à z.
Il est clair que cette expression est différente de :
z = x = y ? a : b
laquelle serait évaluée comme :
z = x = ( y ? a : b )
Bien entendu, une expression conditionnelle peut, comme toute expression, apparaître à son
tour dans une expression plus complexe. Voici, par exemple, une instruction (notez qu’il
s’agit effectivement d’une instruction, car elle se termine par un point-virgule) affectant à z la
plus grande des valeurs de a et de b :
z = ( a>b ? a : b ) ;
De même, rien n’empêche que l’expression conditionnelle soit évaluée sans que sa valeur*
soit utilisée comme dans cette instruction :
a>b ? i++ : i-- ;
Ici, suivant que la condition a>b est vraie ou fausse, on incrémentera ou on décrémentera la
variable i.
13 L’opérateur sizeof
L’opérateur sizeof, dont l’emploi ressemble à celui d’une fonction, fournit la taille en octets
(n’oubliez pas que l’octet est, en fait, la plus petite partie adressable de la mémoire). Par
exemple, dans une implémentation où le type int est représenté sur 2 octets et le type double
sur 8 octets, si l’on suppose que l’on a affaire à ces déclarations :
int n ;
double z ;
• l’expression sizeof(n) vaudra 2 ;
• l’expression sizeof(z) vaudra 8.
Cet opérateur peut également s’appliquer à un type de nom donné. Ainsi, dans l’implémentation
précédemment citée :
• sizeof(int) vaudra 2 ;
• sizeof(double) vaudra 8.
Quelle que soit l’implémentation, sizeof(char) vaudra toujours 1 (par définition, en quelque
sorte).
Cet opérateur offre un intérêt :
• lorsque l’on souhaite écrire des programmes portables dans lesquels il est nécessaire de connaître
la taille exacte de certains éléments ;
• pour éviter d’avoir à calculer soi-même la taille d’objets d’un type relativement complexe
pour lequel on n’est pas certain de la manière dont il sera implémenté par le compilateur. Ce
sera notamment le cas des structures ou des objets.
3 L’instruction switch
3.1 Exemples d’introduction de l’instruction switch
a) Premier exemple
Voyez ce premier exemple de programme accompagné de trois exemples d’exécution.
#include <iostream>
using namespace std ;
main()
{ int n ;
cout << "donnez un entier : " ;
cin >> n ;
switch (n)
{ case 0 : cout << "nul\n" ;
break ;
case 1 : cout << "un\n" ;
break ;
case 2 : cout << "deux\n" ;
break ;
}
cout << "au revoir\n" ;
}
donnez un entier : 0
nul
au revoir
donnez un entier : 2
deux
au revoir
donnez un entier : 5
au revoir
2.3 Imbrication des instructions if
Nous avons déjà mentionné que les instructions figurant dans chaque partie du choix d’une
instruction pouvaient être absolument quelconques. En particulier, elles peuvent, à leur tour,
renfermer d’autres instructions if. Or, compte tenu de ce que cette instruction peut comporter
ou ne pas comporter de else, il existe certaines situations où une ambiguïté apparaît. C’est le
cas dans cet exemple :
if (a<=b) if (b<=c) cout << "ordonné" ;
else cout << "non ordonné" ;
Est-il interprété comme le suggère cette présentation ?
if (a<=b) if (b<=c) cout << "ordonné" ;
else cout << "non ordonné" ;
ou bien comme le suggère celle-ci ?
if (a<=b) if (b<=c) cout << "ordonné" ;
else cout << "non ordonné" ;
La première interprétation conduirait à afficher "non ordonné" lorsque la condition a<=b est
fausse, tandis que la seconde n’afficherait rien dans ce cas. La règle adoptée par le langage
C++ pour lever une telle ambiguïté est la suivante :
Dans notre exemple, c’est la seconde présentation qui suggère le mieux ce qui se passe.
Voici un exemple d’utilisation de if imbriqués. Il s’agit d’un programme de facturation avec
remise. Il lit en donnée un simple prix hors taxes et calcule le prix TTC correspondant (avec
un taux de TVA constant de 19,6 %). Il établit ensuite une remise dont le taux dépend de la
valeur ainsi obtenue, à savoir :
• 0 % pour un montant inférieur à 1 000 euros ;
Un else se rapporte toujours au dernier if rencontré auquel un else n’a pas encore
été attribué.
Les instructions de contrôle
• 1 % pour un montant supérieur ou égal à 1 000 euros et inférieur à 2 000 euros ;
• 3 % pour un montant supérieur ou égal à 2 000 euros et inférieur à 5 000 euros ;
• 5 % pour un montant supérieur ou égal à 5 000 euros.
#include <iostream>
using namespace std ;
main()
{ const double TAUX_TVA = 19.6 ;
double ht, ttc, net, tauxr, remise ;
cout << "donnez le prix hors taxes : " ;
cin >> ht ;
ttc = ht * ( 1. + TAUX_TVA/100.) ;
if ( ttc < 1000.) tauxr = 0 ;
else if ( ttc < 2000 ) tauxr = 1. ;
else if ( ttc < 5000 ) tauxr = 3. ;
else tauxr = 5. ;
remise = ttc * tauxr / 10
0. ;
net = ttc - remise ;
cout << "prix ttc = " << ttc << "\n" ;
cout << "remise = " << remise << "\n" ;
cout << "net à payer = " << net << "\n" ;
}
donnez le prix hors taxes : 500
prix ttc = 598
remise = 0
net à payer = 598
donnez le prix hors taxes : 4000
prix ttc = 4784
remise = 143.52
net à payer = 4640.48
ليست هناك تعليقات:
إرسال تعليق