0% ont trouvé ce document utile (0 vote)
23 vues37 pages

Chap 2 Eléments Du Langage C++

Le document présente les éléments fondamentaux du langage C++, incluant les commentaires, le préprocesseur, et les types de base. Il explique comment utiliser les commentaires pour documenter le code, les directives du préprocesseur pour inclure des fichiers et définir des macros, ainsi que les règles de déclaration des variables et des identificateurs. Enfin, il aborde les types de données en C++ et les implications de la portabilité entre différents systèmes.

Transféré par

Loick Makosso
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
23 vues37 pages

Chap 2 Eléments Du Langage C++

Le document présente les éléments fondamentaux du langage C++, incluant les commentaires, le préprocesseur, et les types de base. Il explique comment utiliser les commentaires pour documenter le code, les directives du préprocesseur pour inclure des fichiers et définir des macros, ainsi que les règles de déclaration des variables et des identificateurs. Enfin, il aborde les types de données en C++ et les implications de la portabilité entre différents systèmes.

Transféré par

Loick Makosso
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

COURS PROGRAMMATION C++ I DIGL L2 2I

Chapitre 2 : Eléments du langage C++

I. Les commentaires
Tout bon programme a des fichiers sources bien commentés pour expliquer comment cela fonctionne et pourquoi certains choix
ont été faits. Ce qui évite une perte de temps lorsque le code source est repris et modifié, soit par un autre développeur, soit par
l'auteur qui ne se souvient pas forcément de son projet s'il n'y a pas touché depuis longtemps.

1. Bloc de commentaire
Un bloc de commentaire est délimité par les signes slash-étoile /* et étoile-slash */ comme en Java et en C#. Exemple :

/*
Un commentaire explicatif sur plusieurs lignes...
*/

Les blocs ne peuvent être imbriqués car dès que le compilateur trouve slash-étoile /*, il recherche la première occurrence
d'étoile-slash */ terminant le commentaire.

Ce code contient une erreur volontaire !

/* : début du commentaire

/* : ignoré

fin du commentaire : */

erreur ici car le commentaire est fini : */

2. Commentaire de fin de ligne


Un commentaire de fin de ligne débute par un double slash // et se termine au prochain retour à la ligne. Exemple :

x++; // augmenter x de 1

Le commentaire ne doit pas paraphraser le code, mais expliquer le rôle de cette partie du code, comme par exemple :

x++; // décaler le point d'un pixel vers la droite

Astuce : La majorité des éditeurs évolués (Visual Studio, Borland C++, Eclipse ...) utilisent ce type de commentaire
pour les commandes commenter/décommenter le groupe de lignes sélectionnées.

II. Le préprocesseur
Avant de compiler le programme, il est possible d'effectuer certaines modifications sur le code source. Le programme
effectuant ces modifications s'appelle le préprocesseur. Les commandes destinées au préprocesseur commencent toutes par #
en début de ligne.

1. Inclusion de fichiers
Pour inclure un fichier à un certain endroit dans le fichier source, on écrit :

1 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

#include "nom_du_fichier"

Le contenu du fichier nom_du_fichier est alors inséré dans le fichier source.


Le nom du fichier peut être écrit entre guillemets "nom_du_fichier" ou entre chevrons <nom_du_fichier>. Dans le premier
cas, cela signifie que le fichier se trouve dans le même dossier que le fichier source, tandis que dans le deuxième cas, il s'agit
d'un fichier se situant dans un endroit différent (ce fichier pouvant être fournit par le compilateur ou une librairie externe par
exemple).

Exemple :

#include <iostream>

Le fichier C++ standard iostream est inclus à cet endroit-là dans le code. Il contient la définition de certains objets standards
notamment cin et cout.

2. #define, #undef
La directive #define permet de remplacer toutes les occurrences d'un certain mot par un autre. Par exemple :

#define N 1143

Sur cet exemple toutes les occurrences de N seront remplacées par 1143. Cela est parfois utilisé pour définir des constantes.
On préférera toutefois utiliser le mot-clé const.
On peut très bien ne pas fixer de valeur et écrire :

#define PLATEFORME_INTEL

La variable de compilation PLATEFORME_INTEL est ici définie. Combiné à #ifdef, on pourra compiler ou non certaines
parties du code à certains endroits du programme.

De la même façon que l'on peut définir une variable, on peut arrêter une définition en utilisant #undef. Son utilisation est
rare, mais peut servir à ne plus définir une variable de compilation. Par exemple:

#undef PLATEFORME_INTEL

3. #ifdef, #ifndef, #if, #endif et #else

Présentation

Toutes ces directives permettent la compilation conditionnelle. C'est-à-dire que la partie du code comprise entre la directive
conditionnelle (#ifdef,
#ifndef ou #if) et la fin du bloc signalée par la directive #endif n'est compilée que si la condition est remplie.

La directive #ifdef permet de compiler toute une série de lignes du programme si une variable de compilation a
précédemment été définie (par la directive #define). La directive #endif indique la fin de la partie de code
conditionnelle. La partie du programme compilée sera toute la partie comprise entre le #ifdef et le prochain #endif.
La directive #ifndef permet de compiler un bout de programme si une variable de compilation n'est pas définie. C'est
donc l'inverse de
#ifdef. La fin de la partie à inclure est déterminée également par #endif.
La directive #if permet de tester qu'une expression est vraie. Cette expression ne peut utiliser que des constantes
(éventuellement définies par une directive #define), et la fonction defined permettant de tester si une variable de
compilation existe.

Il faut noter que les directives #ifdef et #ifndef, bien que très largement utilisées, sont considérées comme dépréciées
("deprecated"). On préférera donc la syntaxe : #if defined(MA_VARIABLE) à #ifdef MA_VARIABLE et #if
!defined(MA_VARIABLE) à #ifndef MA_VARIABLE.

2 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I
Chacune
de ces conditions peut être accompagné d'une directive #else qui permet d'inclure un bout de programme si la condition
n'est pas vérifiée.

Il ne faut pas abuser de ces directives et elles sont surtout utilisées :

pour gérer des problèmes de portabilité.


au début des fichiers d'en-tête pour éviter une double compilation.
dans les fichiers d'en-tête de DLL sous Windows.

Exemples

Exemple 1

#include <iostream> using namespace std; #define FRENCH

int main()
{
#ifdef FRENCH
cout << "BONJOUR";
#else
cout << "HELLO";
#endif
return 0;
}

Dans ce programme, il suffit d'effacer #define FRENCH et de recompiler le programme pour passer d'une version française à
une version anglaise. Ceci pourrait être utile si le programme comporte 10 000 lignes (ce qui est faible pour un programme
réel). Bien évidemment, il existe bien d'autres façons de gérer le multilinguisme en C++.

Exemple 2

Cet exemple montre l'utilisation d'expression dans les définitions de valeurs, et les précautions à prendre pour les expressions.

3 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

// Définir la taille d'un tableau contenant le prix:


// - de 5 variétés d'orange
#define N_ORANGES 5
// - de 3 variétés de pommes
#define N_POMMES 3

#define N_TOTAL_FRUITS N_ORANGES+N_POMMES

double prix_fruits[N_TOTAL_FRUITS]; #if N_TOTAL_FRUITS > 7


// ... plus de 7 variétés de fruits
#else
// ... moins de 7 ou égale à 7 variétés de fruits
#endif

cout << "Total de fruits : " << (N_TOTAL_FRUITS) << endl;


// 8

cout << "Double total de fruits : " << (N_TOTAL_FRUITS * 2) << endl;
// <!> 11 au lieu de 16 car l'opérateur * est prioritaire sur +
// dans l'expression étendue 5 + 3 * 2

// --> Utilisez des parenthèses :


// - autour du nom de la macro
cout << "Double total de fruits : " << ((N_TOTAL_FRUITS) * 2) << endl;
// 16

// - mieux : dans la définition


#define N_TOTAL_FRUITS_MIEUX (N_ORANGES+N_POMMES)
cout << "Double total de fruits : " << (N_TOTAL_FRUITS_MIEUX * 2) << endl;
// 16

Exemple 3

Fichier toto.h

#ifndef TOTO_H #define TOTO_H

... écrire ici les prototypes ... #endif

Le problème :
Imaginons qu'un fichier header toto.h contienne le prototype d'une certaine classe ou d'une fonction. Imaginons que le
programme contiennent 3 autres fichiers headers nommés A.h, B.h et C.h qui ont tous les 3 besoin des prototypes inclus dans
toto.h, ces 3 fichiers vont commencer par #include"toto.h". Imaginons également que C.h a besoin des prototypes inclus dans
A.h et B.h. C.h va donc commence par #include"A.h" et #include"B.h". Le problème est que le fichier toto.h va être inclus
plusieurs fois. Le compilateur va alors refuser de compiler le programme en indiquant que plusieurs prototypes d'une même
fonction sont inclus.

Solution :

Pour résoudre l'inclusion multiple de fichier headers (inévitable), on va faire commencer le fichier header par #ifndef TOTO_H
et il se termine par #endif. Si la variable de compilation TOTO_H n'est pas définie, alors le header sera inclus, sinon, il sera
tout simplement vide. Juste après #ifndef TOTO_H, nous allons écrire #define TOTO_H définissant justement cette variable
de compilation TOTO_H.
La première fois que le header sera inclus, TOTO_H n'est pas défini, le header normal sera donc inclus. #define TOTO_H
définira alors la variable de compilation TOTO_H. La deuxième fois que ce même header sera inclus, et les fois suivantes,
TOTO_H sera défini et par conséquent, le header sera vide. les prototypes n'auront donc été inclus qu'une seule fois. Le tour
est joué. Il faut donc faire commencer systèmatiquement (c'est tout du moins conseillé) tous les fichiers header par les 2 lignes
#ifndef ... et #define ... et les faire se terminer par #endif.

4 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I
Autre solution

Toutefois, il existe une autre solution au problème précédent en utilisant la directive suivante en début de fichier .h :

#pragma once

Cette directive indique au compilateur d'ignorer le fichier s'il a déjà été "visité", et ne fonctionne qu'avec certains compilateurs
:

Visual C++
CodeWarrior
GCC for Darwin

4. Les macros

Présentation

Les macros sont des #define particulier parce qu'ils contiennent des paramètres. Ainsi si vous écrivez :

#define AFFICHE(x) cout << x << endl;

Alors vous pouvez écrire AFFICHE("BONJOUR") et le préprocesseur modifiera cette ligne et la transformera en cout <<
"BONJOUR" << endl;. Il y aura substitution de x par "BONJOUR". Il ne faut pas abuser des macros et très souvent l'utilisation
de fonctions, notamment les fonctions inline, est préférable.

Exemple

#include <iostream>
using namespace std;

#define AFFICHER(x) cout << x << endl; int main()


{
AFFICHER("BONJOUR");
return 0;
}

Bonnes pratiques

Afin d'utiliser correctement les macros, il est préférable de les afficher clairement et de les rendre suffisamment flexible à
différentes utilisations. Si la macros est constituée de plusieurs instructions séparés par des ;, il est préférable d'écrire la macro
sur plusieurs lignes afin d'accroître sa lisibilité. Pour indiquer à une macro que sa définition continue sur la ligne suivante, il
suffit d'indiquer un antislash ('\') en dernier caractère le la ligne.

#define AFFICHER(x) \ cout << x; \


cout << endl;

L'utilisation la plus courante des macros est de ne pas mettre de ; à la fin de celle-ci, mais de le mettre dans le code, là où elle
est utilisée. En effet, on peut prévoir qu'une macro soit utilisable en tant qu'instruction simple, ou en tant que condition ou
paramètre de fonction où l'on ne doit pas mettre de ;. Pour les macros qui ne retournent rien (comme la macro AFFICHER
dans l'exemple précédent), le placement du ; n'est pas un problème car elles ne retournent rien et ne seront jamais utilisées
dans une condition ou un appel de fonction.

#include <iostream>
using namespace std;

5 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

#define DIVISER(x, y) ((x) / (y)) int main()


{
int valeur = DIVISER(5, 3);

if (DIVISER(8, 2) == 4)
cout << DIVISER(1.0, 5.0) << endl;

return 0;
}

Il est fortement conseillé de toujours utiliser un paramètre de macro avec des parenthèses autour. Si l'on reprend l'exemple précédent sans parenthèses :

#include <iostream>
using namespace std;

#define DIVISER(x, y) x / y int main()


{
int valeur = DIVISER(5, 3);

if (DIVISER(4 + 4, 2) == 4)
cout << DIVISER(1.0, 5.0) << endl;

return 0;
}

Ici, le résultat obtenu n'est pas forcement celui désiré. DIVISER(4 + 4, 2) sera traduit après la précompilation par 4 + 4 /
2. Ceci donne pour valeur 4 + 2, soit 6. Ajouter un maximum de parenthèses permet de s'assurer de la validité de la macro sous
plusieurs utilisations différentes. Ainsi, dans l'exemple précédent, une utilisation de parenthèses dans la macro (#define
DIVISER(x, y) ((x) / (y))), aurait traduit DIVISER(4 + 4, 2) en ((4 + 4) / (2)). Ceci aurait donné comme valeur 8
/ 2 = 4, la valeur attendue.

III. Les types de base et les déclarations


1. Déclarations, types et identificateurs

Les variables

Comme la plupart des langages de programmation, le C++ utilise la notion de variable. Une variable peut être vue comme une
zone de la mémoire qui comprend une certaine valeur.

Les types en C++ et les systèmes de représentation

Le langage C++ impose un mécanisme de type pour indiquer la nature des données contenues dans une variable. Ainsi un
double permettra de stocker un réel et un int permettra de stocker un entier. Par contre, il ne définit pas de système de
représentation pour représenter ces variables. Ainsi, le standard ne spécifie pas comment on représente un double sous la
forme d'une suite de bits. Le système de représentation utilisé peut donc varier entre deux ordinateurs ou en fonction du
compilateur utilisé. Cette particularité peut parfois poser de graves problèmes de portabilité d'un programme.

Les déclarations

Toute variable en C++ doit être déclarée : la déclaration indique l'identificateur de la variable (son nom) et sa nature (son
type).

Syntaxe :

6 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

type identificateur;

Exemple :

int a;

Cette déclaration définit une variable d'identificateur a qui contient un entier de type int.

Identificateurs valides

Un identificateur est une suite de caractères (pouvant être majuscules ou minuscules), de chiffres ou d'underscores
(underscore ou "tiret bas" est le caractère _). Cette suite ne peut pas commencer par un chiffre. Un identificateur ne peut
contenir ni espace, ni tiret - (utilisé pour l'opération de soustraction).

Les types entiers

Le langage C++ possède plusieurs types de base pour désigner un entier.

int : contient un entier de taille normale, positif ou négatif.


short int : contient un entier de petite taille, positif ou négatif.
long int : contient un entier de grande taille (32 bits), positif ou négatif.
long long int : contient un entier de plus grande taille (64 bits), positif ou négatif.
unsigned int : contient un entier de taille normale, positif ou nul.

unsigned short int : contient un entier de petite taille, positif ou nul.

unsigned long int : contient un entier de grande taille (32 bits), positif ou nul.

unsigned long long int : contient un entier de plus grande taille (64 bits), positif ou nul.

La longueur d'un long int , d'un int et d'un short int n'est pas spécifié par le langage. Plus un entier est représenté sur un
grand nombre de bits, plus il pourra être grand. Ainsi, il est usuel de représenter un int sur 32 bits : il peut alors
représenter n'importe quel entier entre -231 et 231-1. Le langage impose juste que la taille d'un long int doit être
supérieure ou égale à celle d'un int et que la taille d'un int doit être supérieure ou égale à celle d'un short int !

Le système de représentation utilisé non plus. En général, la base 2 est utilisée pour les types unsigned int, unsigned long
int et unsigned short int et le complément à 2 est utilisé pour les types int, long int et short int. Ce n'est toutefois nullement
obligatoire.

Interprétation des constantes entières

si une constante commence par 0x, elle sera interprétée comme une valeur en hexadécimal (base 16).
si une constante commence par 0 suivi d'un chiffre, elle sera interprétée comme
valeur en octal (base 8). dans le cas contraire, elle sera interprétée comme étant
en base 10.

Exemples :

98 représente 98 en base 10.


0x62 représente 98 en hexadécimal
(6*16+2). 0142 représente 98 en
octal (1*64+4*8+2).

si une constante entière se termine par un U, elle sera interprétée comme


étant un unsigned. si une constante entière se termine par un L, elle sera
interprétée comme un long.
si une constante entière se termine par LL, elle sera interprétée comme un long long.

7 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

Exemples
▪ 78U représente le unsigned int valant 78.
▪ 78L représente le long int valant 78.
▪ 78LL représente le long long int valant 78.
▪ 78ULL représente le unsigned long long int valant 78.

Les types réels

Pour représenter un réel, il existe 3 types de base :

float (simple précision)


double (double précision)
long double (précision étendue)

Le langage ne précise pas ni le système de représentation, ni la précision de ces différents formats. Le type long double
est juste censé être plus précis que le double, lui-même plus précis que le float.
Il est toutefois usuel (mais non obligatoire) de représenter le float sur 32 bits dans le format IEEE 754 simple précision
et le double sur 64 bits dans le format IEEE 754 double précision.

Interprétation des constantes réelles

On peut écrire un réel sous la forme 1.87 ou .56 ou 8. ou 7.6e10 ou 5.4e-3


ou encore 10.3E+2. Une constante qui se termine par un f ou un F sera
interprétée comme un float.
Une constante qui se termine par un l ou un L sera interprétée comme un long double.

Exemples :

3.65L représente le long double valant 3.65.


3.65F représente le float valant 3.65.

Les caractères

Le caractère est l'élément de base de tout texte et donc sans doute de toute pensée. Les changements de l'informatique
ont conduit à différentes approches pour représenter un caractère sur cinq, sept, huit, seize, dix-sept ou trente-deux bits.

Aujourd'hui, les deux types incontournables sont le type char hérité du C, et ceux relatifs à Unicode.

Ce chapitre traite des types de bases utiles pour représenter des caractères. Toutefois les caractères sont rarement utilisés
seuls, surtout dans les applications Unicode. À ce sujet, vous trouverez dans la suite de cet ouvrage les chapitres suivants :

Programmation C++/La_librairie_standard#Les chaînes de


caractères Programmation C++/Les_tableaux#Les
tableaux de caractères

Le type char

Il s'agit du type historique pour représenter un caractère. Bien que le type char fasse penser à un caractère (character en
anglais), il désigne souvent de facto un octet, qui peut être signé ou non signé suivant le compilateur. C'est l'un des
concepts que le C++ a repris du langage C. Aucun système de représentation n'est imposé pour les caractères et on utilise
en général des dérivés (8 bits) du code ASCII (qui est un code sept bits). Un caractère doit juste être codé sur au moins 8
bits : le standard C++ reprend les mêmes propriétés requises que le langage C dont le standard pour les types entiers
(dont fait partie le type char) ne spécifie pas explicitement la taille en octets mais l'intervalle à supporter au minimum.

Le type char signé doit supporter les valeurs entre -127 et +127 [1], et le type char non signé doit supporter les valeurs
entre 0 et 255. La variante signée requiert donc le support de 255 valeurs au lieu de 256, car un nombre signé peut être
représenté de plusieurs façons[2] pouvant impliquer deux représentations possibles pour la valeur zéro (-0 et +0) ; ceci
n'est pas le cas de la représentation en complément à 2 permettant de supporter un intervalle de -128 à +127.

8 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

Usuellement, le type char est exactement 8 bits, ce qui fait que c'est le seul type utilisé pour représenter un octet, créant
ainsi une confusion entre caractère et octet. Le chapitre suivant s'intéresse au huitième bit.

Exemple :

char c;
c = 'E';

Le caractère E est transféré dans la variable c de type char .


On peut transférer un char dans un int pour récupérer son codage.
Ainsi, on peut écrire :

int a;
char b;
b = 'W';
a = b;

On récupère alors dans a le codage du caractère 'W'. On récupérera donc en général le code ASCII de 'W' dans la variable a.
Aujourd'hui, il est désuet de considérer que l'on code tous caractères sur un seul char. Le type char reste cependant
incontournable car il est souvent utilisé pour désigner un octet.

Transformation de majuscule en minuscule

#include<iostream>
using namespace std;

int main()
{
char a, b;
cout<<"Tapez un caractere : "; cin>>a;
if (a>='A' && a<='Z') {
cout<<"Vous avez tapé une majuscule."<<endl;
b = a + ('a'-'A');
cout<<"La minuscule correspondante est "<< b <<endl;
}
else if (a>='a' && a<='z') {
cout<<"Vous avez tapé une minuscule."<<endl;
b = a + ('A'-'a');
cout<<"La majuscule correspondante est "<< b <<endl;
}
else cout<<"Vous n'avez pas tapé une lettre."<<endl;
return 0;
}

Explications

On demande à l'utilisateur de taper un caractère dans une variable a.


Si l'utilisateur a tapé une majuscule, on affiche la minuscule
correspondante. Si l'utilisateur a tapé une minuscule, on
affiche la majuscule correspondante.

Exécution 1

Tapez un caractère : H
Vous avez tapé une majuscule.
La minuscule correspondante est h.

Exécution 2

9 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I
Tapez un
caractère : w
Vous avez tapé une minuscule.
La majuscule correspondante est W.

Exécution 3

Tapez un caractère : 9
Vous n'avez pas tapé une lettre.

Les types signed char et unsigned char

Lorsqu'on transfère un char dans un int, peut-on récupérer une valeur négative ? La réponse est oui si on utilise le
type signed char et non si on utilise le type unsigned char. Ces types peuvent être utile lorsqu’on manipule des caractères non ASCII.

Pour les données de type char, lorsque ni signed ni unsigned ne sont précisés, le choix entre les deux est fait par le
compilateur. Dans tous les cas à l'époque où seuls les codages ASCII et autres codages ISO-646 étaient utilisés cela
n'avait pas d'importance.
Aujourd'hui cependant, quasiment tous les codages de caractères utilisent a minima huit bits. C'est notamment le cas
d'UTF-8. L'éventuel bit de signe doit donc être considéré pour permettre la portabilité du logiciel.

Les types char16_t and char32_t

À partir de C++11 (C++ norme de 2011) trois types de chaînes de caractères sont prise en charge: UTF-8, UTF-16, et UTF-
32. Le type char conserve ses unités de codage de huit bits pour le codage des caractères Unicode via UTF-8, les nouveaux
types char16_t et char32_t sont des unités de codage de seize ou trente-deux bits pour le codage des caractères Unicode
via UTF-16 ou UTF-32.

Ces types sont standard à partir de C++2011 mais n'existent pas sur des compilateurs plus anciens, ni même sur les compilateurs
C-2011.

Le type wchar_t

Ce type de caractère n'existe qu'avec les compilateurs supportant l'Unicode (jeu de caractère international standard
couvrant les langues du monde entier). Ces caractères sont stockés sur 2 octets ou 4. Les valeurs constantes de caractère
(entre simple quote) ou de chaîne de caractères (entre double quote) doivent alors être précédées du caractère L.
Exemple :

wchar_t a = L'é'; // caractère 'é' unicode(16 ou 32 bits);


wchar_t[] chaine = L"Bonjour, monde !"; // chaîne de caractère unicode

Ce type présente le problème de ne pas être standard: certaines implémentations n'offrent que 16 bits soit une portion limitée
des caractères Unicode.

API exceptionnellement Unicode

Certaines API C++ telle que Visual C++ sous Windows sont paramétrables par une option dite unicode ou non. Pour cela
elles se basent sur le type de caractèreTCHAR que le compilateur interprète (en fait remplace) par char ou wchar_t selon
l'option Unicode. Dans ce cas, les valeurs constantes de chaînes et de caractères doivent être encadrées par la macro _T.
Cette macro peut alors faire précéder les constantes d'un caractère L ou non.
Exemple :

TCHAR a = _T('é'); // caractère 'é' unicode ou huit bits;


TCHAR[] chaine = _T("Bonjour, monde !"); // chaîne de caractère unicode ou ascii (huit bits)

10 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I
Les booléens

Le C++ utilise le type bool pour représenter une variable booléenne. Un booléen ne peut prendre que 2 valeurs : true
ou false. On peut affecter à un booléen le résultat d'une condition, ou l'une des deux constantes citées précédemment.

Exemple :

bool a;
int c;
c = 89;
a = (c > 87);
// a reçoit alors la valeur true.

L'opérateur const

L'opérateur const placé devant une déclaration de variable décrit celle-ci comme constante ; elle ne peut être changée.

Exemple :

int x = 2;
const int y = 2;
x += 1; // x recevra la valeur 3
y += 1; // Cette opération est interdite par un compilateur C++ conforme

Dans cet exemple, nous déclarons deux variables x et y, chacune avec la même valeur. La première variable (x) peut être
modifiée, ce qui n'est pas le cas de la seconde (y) qui est déclarée const. Le compilateur refusera toute opération qui
tenterait d'en modifier son contenu (opérations d'assignation et d'incrémentation =, +=, -=, *=, /=, >>=, <<=, ++).
Il en va de même pour les objets. Un objet déclaré const ne pourra pas être modifié, c'est-à-dire que le compilateur
refusera l'invocation d'une méthode non const sur cet objet.

Exemple :

class X {
public:
X() : valeur_(0) {}
explicit X(int valeur) : valeur_(valeur) {}
void annule() { this->valeur_ = 0; }
void init(int valeur) { this->valeur_ = valeur; }
int valeur() const { return this->valeur_; }
private:
int valeur_;
};

X x(2)
const X y(5);
x.annule(); // Ok
y.annule(); // Erreur de compilation.

Dans cet exemple, deux objets x et y ont été déclarés. Il est possible d'invoquer n'importe quelle méthode (const ou non)
sur l'objet x, par contre seules les méthodes const peuvent être invoquées sur l'objet y puisqu'il est déclaré const.

L'opérateur sizeof
L'opérateur sizeof permet de savoir le nombre d'octets qu'occupe en RAM une certaine variable. On peut écrire sizeof(a)
pour savoir le nombre d'octets occupé par la variable a. On peut aussi écrire, sizeof(int) pour connaître le nombre d'octets
occupés par une variable de type int. Cet opérateur est très utile lorsqu'on veut résoudre des problèmes de portabilité
d'un programme.

Plus précisément sizeof(char) vaut toujours 1, par définition. Sur la plupart des architectures un char est codé par
huit bits, soit un octet. Programmation C/Types de base#Caractères

11 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

Définir un alias de type


L'instruction typedef permet de définir un alias pour un type de données. Ceci permet dans certains cas de raccourcir le code, et dans tous les cas
c'est l'occasion de donner un nom plus explicite à un type. Ceci favorise une meilleur lecture du code.

La syntaxe de typedef est exactement la même que celle de la déclaration d'une variable, excepté que l'instruction commence par typedef et
qu'aucune variable n'est réservée en mémoire, mais un alias du type est créé.

Exemple:

typedef unsigned long data_size;

data_size readData(char* buffer, data_size buffer_size);

On s'aperçoit plus facilement que la fonction retourne le nombre d'octets lus que dans la déclaration sans typedef :

unsigned long readData(char* buffer, unsigned long buffer_size);

IV. Les opérations de base

Le langage C++ possède un grand nombre d'opérateurs de base effectuant entre autres des opérations arithmétiques et
capables de travailler sur les entiers, les réels, etc... Nous allons présenter ici ces principaux opérateurs.

1. L'affectation

Syntaxe

identificateur=expression

Sémantique

On commence par évaluer l'expression et on met le résultat dans la variable identificateur.

Exemple

#include <iostream>
using namespace std;

int main()
{
int a, b, s;
cout << "Tapez la valeur de a : "; cin >> a;
cout << "Tapez la valeur de b : "; cin >> b;

s = a + b; // affecter le résultat de l'addition à la variable s

cout << "La somme a+b vaut : " << s << endl;
return 0;
}

Exécution

Tapez la valeur de a : 45
Tapez la valeur de b : 67
La somme a+b vaut : 112

12 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I
Explications

Dans ce programme, on déclare 3 variables a, b et s. On demande à l'utilisateur du programme de taper la valeur de a


puis la valeur de b. cout sert à l'affichage à l'écran et cin à la saisie au clavier. Le programme calcule ensuite dans la
variable s la somme a+b. On affiche finalement la valeur de s.

Affectation à la déclaration

L'affectation à la déclaration d'une variable est appelée "déclaration avec initialisation", car est un cas particulier de
l'affectation (en particulier avec les objets).

Exemple :

int a = 57;

Affectation en série

Le résultat d'une même expression peut être assigné à plusieurs variables sans la réévaluer, en une seule instruction :

identificateur2=identificateur1=expression

Par exemple :

a = b = 38 + t;

L'opération se déroule de la manière suivante :

a = (b = 38 + t);

1. Le résultat de 38+t est calculé ;


2. Il est affecté à la variable b ;
3. Il est retourné par l'opérateur d'affectation ;
4. Cette valeur retournée est affectée à la variable a.

Remarque importante

Pour effectuer un test de comparaison, par exemple comparer a à 53, il ne faut pas écrire if(a=53) mais if(a==53) en
utilisant 2 fois le symbole =. Une erreur classique !

2. Opérations arithmétiques

sur les entiers

On peut effectuer les opérations arithmétiques usuelles sur les entiers en utilisant les opérateurs +, -, / et *. Il faut juste
avoir en tête que la division sur les entiers effectue une troncature (la partie décimale est perdue). Le modulo s'obtient
en utilisant l'opérateur %. Ainsi a%b désigne le reste de la division de a par b. On peut utiliser les parenthèses pour fixer
l'ordre d'évaluation des expressions. On peut aussi utiliser l'opérateur ++ pour incrémenter une variable de 1. L'opérateur
--, quant à lui, décrémente une variable de 1.

sur les réels

Sur les réels, on utilise les opérateurs +, -, * et / pour effectuer les 4 opérations de base. Il faut avoir en tête que toute
opération sur les réels est entâchée d'une minuscule erreur de calcul : il s'agit d'un arrondi sur le dernier bit. Si on effectue
de nombreux calculs, cette erreur peut s'amplifier et devenir extrêmement grande.

13 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I
Les
fonctions mathématiques habituelles sont présentes dans la bibliothèque standard <cmath>.

Exemple

#include <iostream>
using namespace std;

int main()
{
int a, b, M;

cout << "Tapez la valeur de a : "; cin >> a;


cout << "Tapez la valeur de b : "; cin >> b;

M = (a+3) * (6+a) + (b-5) * 2;

cout << "M vaut " << M << endl;

return 0;
}

Exécution :

Tapez la valeur de a : 1
Tapez la valeur de b : 2
M vaut 22

3. Opérations binaires
Les opérations présentées dans cette section opèrent au niveau des bits.

Les décalages

Les décalages permettent de décaler vers la droite ou vers la gauche toute la représentation en binaire d'une valeur d'un

Syntaxe Sémantique
valeur << Décale la valeur de decalage bits
decalage vers la gauche.
valeur >> Décale la valeur de decalage bits
decalage vers la droite.

Exemple

14 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

#include <iostream>
using namespace std;

int main()
{
int a = 5;
int b;

b = a << 3;
cout<<"b vaut : "<<b<<endl;

b = (a+1) << 3;
cout<<"b vaut : "<<b<<endl;

return 0;
}

Exécution

b vaut 40
b vaut 48

Le ET binaire

Syntaxe

exp1 & exp2

Sémantique

Le résultat est obtenu en faisant un ET logique sur chaque bit de la représentation de exp1 avec le bit correspondant de
la représentation de exp2. Le ET logique a pour résultat 1 si les deux bits correspondants en entrée sont à 1 (pour 1-1),
et sinon 0 (pour 0-1, 1-0 ou 0-0).

Exemple

#include <iostream>
using namespace std;

int main()
{
int a = 53;
int b = 167;
int c = a & b;
cout << "c vaut " << c << endl;

return 0;
}

Exécution

c vaut 37

15 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I
Explications
53 s'écrit en binaire 0000 0000 0000 0000 0000 0000 0011 0101
167 s'écrit en binaire 0000 0000 0000 0000 0000 0000 1010 0111

ET binaire 0000 0000 0000 0000 0000 0000 0010 0101


==> le résultat vaut 37 en décimal

Le OU binaire

Syntaxe

exp1 | exp2

Sémantique

Le résultat est obtenu en faisant un OU logique sur chaque bit de la représentation de exp1 avec le bit correspondant de
la représentation de exp2. Le OU logique a pour résultat 1 si l'un des deux bits correspondant en entrée est à 1 (pour 1-1,
1-0 ou 0-1), et sinon 0 (pour 0-0).

Exemple

#include <iostream>
using namespace std;

int main()
{
int a = 53;
int b = 167;
int c = a | b;
cout << "c vaut " << c << endl;

return 0;
}

Exécution

c vaut 183

Explications

53 s'écrit en binaire 0000 0000 0000 00000 0000 0000 0011 0101
167 s'écrit en binaire 0000 0000 0000 00000 0000 0000 1010 0111
OU binaire 0000 0000 0000 00000 0000 0000 1011 0111
==> le résultat vaut 183 en décimal

Le OU exclusif binaire

Syntaxe

exp1 ^ exp2

16 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I
Sémantique

Le résultat est obtenu est faisant un OU exclusif sur chaque bit de la représentation de exp1 avec le bit correspondant de
la représentation de exp2. Le OU exclusif logique a pour résultat 1 si les deux bits correspondant en entrée sont
différents (pour 1-0 ou 0-1), et sinon 0 (pour 1-1 ou 0-0).

Exemple

#include <iostream>
using namespace std;

int main()
{
int a = 53;
int b = 167;
int c = a ^ b;
cout << "c vaut " << c << endl;

return 0;
}

Exécution

c vaut 146

Explications

53 s'écrit en binaire 0000 0000 0000 00000 0000 0000 0011 0101
167 s'écrit en binaire 0000 0000 0000 00000 0000 0000 1010 0111
OU exclusif binaire 0000 0000 0000 00000 0000 0000 1001 0010
==> le résultat vaut 146 en décimal

Le NON binaire

Syntaxe

~exp

Sémantique

Le résultat est obtenu en inversant tous les bits de la représentation de exp. Le 1 devient 0 et vice versa.

Exemple

#include <iostream>
using namespace std;

int main()
{
int a = 53;
int b = ~a;
cout << "b vaut " << b << endl;
return 0;
}

17 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I
Exécution

b vaut -54

Explications

53 s'écrit en binaire 0000 0000 0000 0000 0000 0000 0011 0101
NON binaire 1111 1111 1111 1111 1111 1111 1100 1010
==> le résultat vaut -54 en décimal (signed int)
ou 4294967242 (unsigned int)

4. Opérations booléennes

Comparaisons usuelles

Les comparaissons usuelles s'effectuent selon la syntaxe a symbole b, symbole désignant le test effectué :

Le symbole > désigne le test strictement


supérieur à. Le symbole >= désigne le
test supérieur ou égal à. Le symbole <
désigne le test strictement inférieur à. Le
symbole <= désigne le test inférieur ou
égal à.

Le symbole == désigne le test d'égalité.


Le symbole != désigne le test différent.

Le ET logique

Syntaxe

condition1 && condition2

Sémantique

Le ET logique est vrai si à la fois les condition1 et condition2 sont vraies . Il est faux dans le cas contraire.

Attention : condition2 est évalué uniquement si condition1 a la valeur true (différente de zéro). Cette évaluation élimine
l’évaluation inutile de
condition2 puisque l’expression estfalse de par le fait que condition1 est false. Il s’agit d' une évaluation
de court-circuit.

Le OU logique

Syntaxe

condition1 || condition2

Sémantique

Le OU logique est vrai si au moins une des 2 conditions condition1 ou condition2 est vraie . Il est faux dans le cas contraire.

Attention : Le conditions2 est évalué uniquement si condition1 a la valeur false. . Il s’agit là aussi d' une évaluation de court-circuit .

18 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

Le NON logique

Syntaxe

!(condition)

Sémantique

Le NON logique inverse la valeur de condition: si condition vaut true le résultat vaut false. Si condition vaut false le résultat vaut
true.

Exemples d'utilisation des opérateurs booléens

bool b;
int u = 18;
b = !( (u>20 || (u<0)));

b vaut true.

bool b = ! false ; // -> b vaut true


b = ! true ; // -> b vaut false

5. Affectation avec opérateur


Il existe toute une gamme d'opérateurs permettant d'effectuer une opération (une addition par exemple) avec le contenu
d'une variable et de mettre le résultat dans cette même variable. Ainsi il sera plus pratique d'écrire b += a ; que
d'écrire b = b + a ;

Une opération du type

a opérateur= expression;

équivaut à :

a = a opérateur (expression);

L'opérateur

+= Syntaxe

identificateur+=expression

Sémantique

Cet opérateur ajoute à la variable identificateur la valeur de expression et stocke le résultat dans la variable identificateur.

Exemple

19 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

#include <iostream>

using namespace std;

int main()
{
int a = 80;
int b = 70;
b += a;
cout << "La valeur de b est : " << b << endl;
return 0;
}

Exécution

La valeur de b est 150

Autres opérateurs

Sur le même modèle, on peut utiliser les opérateurs suivants : -=, *=, /=, %=, >>=,<<=, &=, |= et ^=.

6. Priorité des opérateurs


Comme en mathématique, tous les opérateurs n'ont pas la même priorité.
Par exemple, l'expression 1+2*3 retournera la valeur 7 car l'opérateur * a une plus grande priorité et est évalué avant
l'opérateur +. L'expression est donc équivalent à 1+(2*3).

Les parenthèses permettent de modifier les priorités en encadrant ce qu'il faut évaluer avant. Ainsi l'expression (1+2)*3
retournera la valeur 9.

7. La liste complète des opérateurs du C++


La liste ci-dessous présente les différents opérateurs du C++ avec leur associativité dans l'ordre de leur priorité (du
premier évalué au dernier). Les opérateurs situés dans le même bloc ont la même priorité. Les opérateurs en rouge ne
peuvent être surchargés.

20 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I
Opérateurs Description Associativité
:: Sélection d'un membre d'un espace de nom, ou d'un membre de gauche à
statique d'une classe droite
() Parenthèses pour évaluer en priorité

[] Tableau
. Sélection d'un membre par un identificateur (structures et
-> objets) Sélection d'un membre par un pointeur (structures et
objets)
++ -- Incrémentation post ou pré-fixée de droite à
gauche
+- Opérateur moins unaire
! ~ Non logique et NON logique bit à
(type) bit cast
* Déréférencement
& Référencement (adresse d'une
sizeof variable) Taille d'une variable / d'un
new type Allocation mémoire
delete Libération mémoire
delete[] Libération mémoire d'un tableau
.* Déréférencement d'un pointeur de membre d'un objet de gauche à
droite
->* Déréférencement d'un pointeur de membre d'un objet pointé
*/% Multiplication, division, et modulo (reste d'une division)
+- Addition et soustraction
<< >> Décalage de bits vers la droite ou vers la gauche
< <= Comparaison “ inférieur strictement ” et “ inférieur ou égal ”

> >= Comparaison “ supérieur strictement ” et “ supérieur ou égal ”


== != Condition “ égal ” et “ différent ”
& ET logique bit à bit
^ OU exclusif bit à bit
| OU logique bit à bit
&& ET logique booléen
|| OU logique booléen
c?t:f Opérateur ternaire de condition de droite à
= Affectation gauche

+= -= Affectation avec somme ou soustraction


*= /= %= Affectation avec multiplication, division ou
<<= >>= modulo Affectation avec décalage de bits
&= ^= |= Affectation avec ET logique, OU logique ou OU exclusif bit à
bit
, Séquence d'expressions de gauche à
droite

V. Les entrées-sorties

1. Classes de gestion des flux


Les entrées et sorties sont gérées par deux classes définies dans le fichier d'en-tête <iostream> :

ostream (Output stream) permet d'écrire des données vers la console, un fichier, ... Cette classe surdéfinit l'opérateur
<<.
istream (Input stream) permet de lire des données à partir de la console, d'un fichier, ... Cette classe surdéfinit
l'opérateur >>.

21 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

2. Flux standards
Trois instances de ces classes représentent les flux standards :

cout écrit vers la sortie standard,


cerr écrit vers la sortie d'erreur,
clog écrit vers la sortie technique,
cin lit à partir de l'entrée standard (jusqu'au premier espace exclu, éventuellement). Demander un nombre et y
entrer des lettres provoque une erreur.
getline lit à partir de l'entrée standard (tout).

Ces objets sont définis dans l'espace de nom std.

Exemple

#include <iostream>

using namespace std;

int main()
{
int n;
cout << "Entrez un nombre positif : ";
cin >> n;
if (n<0) cerr << "Erreur: Le nombre " << n
<< " n'est pas positif " << endl;
else cout << "Vous avez entré " << n << endl;
return 0;
}

3. Autres types de flux


Les instances des classes dérivées des classes istream et ostream sont également manipulés avec les opérateurs << et
>>. Cependant, il ne faut pas oublier de les fermer en appelant la méthode close().
Note: Les noms de fichiers sont codés sur 8 bits sous Linux/Unix et sur 16 bits sur Windows, ce qui peut induire des
problèmes de portabilité, le cas échéant.

Flux de fichier

La classe ifstream permet de lire à partir d'un fichier. Le constructeur a la syntaxe suivante :

ifstream(const char* filename, openmode mode=in)

Le paramètre mode peut être une combinaison des valeurs suivantes :


app
(append) Placer le curseur à la fin du fichier avant écriture.
ate
(at end) Placer le curseur à la fin du fichier.

binary
Ouvrir en mode binaire plutôt que texte.
in Autoriser la lecture.

out Autoriser l'écriture.

22 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

trunc

(truncate) Tronquer le fichier à une taille nulle.

Exemple 1 : lire un entier depuis un fichier

ifstream fichier("test.txt");
int a;
fichier >> a; // lire un entier
cout << "A = " << a;
fichier.close();

Exemple 2 : afficher tous les caractères d'un fichier

ifstream fichier("test.txt");
while (fichier.good())
cout << (char) fichier.get();
fichier.close();

La classe ofstream permet d'écrire vers un fichier. Son constructeur a une syntaxe similaire :

ofstream(const char* filename, openmode mode=out|trunc)

Exemple :

ofstream fichier("test.txt");
fichier << setw(10) << a << endl;
fichier.close();

La classe fstream dérive de la classe iostream permettant à la fois la lecture et l'écriture. Cette dernière
(iostream) dérive donc à la fois de la classe ostream et de la classe istream. Son constructeur a la syntaxe
suivante :

fstream(const char* filename, openmode mode=in|out)

Exemple :

fstream fichier("test.txt");
fichier << setw(10) << a << endl;
fichier.seekg(0, ios_base::beg);
fichier >> b;
fichier.close();

Flux de chaîne de caractères

Ces flux permettent d'écrire pour produire une chaîne de caractères, ou de lire à partir d'une chaîne de caractères.
La classe istringstream dérivée de istream permet de lire à partir d'une chaîne de caractères, et possède deux
constructeurs :

istringstream ( openmode mode = in );


istringstream ( const string & str, openmode mode = in );

Exemple :

23 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

int n, val;
string stringvalues;
stringvalues = "125 320 512 750 333";
istringstream iss (stringvalues, istringstream::in);

for (n = 0; n < 5; n++)


{
iss >> val;
cout << val << endl;
}

La classe ostringstream dérivée de ostream permet d'écrire pour créer une chaîne de caractères, et possède
également deux constructeurs :

ostringstream ( openmode mode = out );


ostringstream ( const string & str, openmode mode = out );

Le second permet de spécifier le début de la chaîne de caractères produite. La méthode str() retourne la

chaîne de caractères produite.

Exemple :

ostringstream oss (ostringstream::out);


int a = 100;
oss << "Test d'écriture a=" << a << "\n";
cout << oss.str();

La classe stringstream dérivée de iostream permet d'écrire et lire, et possède deux constructeurs :

stringstream ( openmode mode = in | out );


stringstream ( const string & str, openmode mode = in | out );

Exemple :

int n, val;
stringstream ss (stringstream::in | stringstream::out);

// écriture
ss << "120 42 377 6 5 2000";

// lecture
for (int n = 0; n < 6; n++)
{
ss >> val;
cout << val << endl;
}

4. Manipulateurs
Le fichier d'en-tête <iomanip> définit des manipulateurs de flux tels que endl, hex. Ces manipulateurs
modifient la façon d'écrire ou lire les données qui suivent celui-ci.

24 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

Manipulateur endl

Ce manipulateur écrit un retour à la ligne dans le flux, quel qu'il soit (\r\n pour Windows, \n pour
Unix/Linux, \r pour Mac, ...). Il est donc conseillé de l'utiliser au lieu du/des caractère(s) correspondant(s),
si la portabilité de votre application joue un rôle important.
Exemple:

cout << "Une première ligne" << endl << "Une deuxième ligne" << endl;

N.B.: Certains compilateurs C++ (notamment Visual C++) ne supporte pas que le manipulateur endl soit
suivi d'autres données à écrire. Dans ce cas, il faut écrire les données suivantes dans une nouvelle instruction
:

cout << "Une première ligne" << endl;


cout << "Une deuxième ligne" << endl;

Manipulateur hex

Ce manipulateur indique que les prochains entiers sont à lire ou écrire en base hexadécimale.

Manipulateur dec

Ce manipulateur indique que les prochains entiers sont à lire ou écrire en base décimale.

Manipulateur setbase(base)

Les 2 manipulateurs précédents sont des alias de celui-ci, qui permet de spécifier la base

des prochains entiers à lire ou écrire.

Exemple :

int a = 200; // 200 en décimal

cout << "Valeur de a en base 16 = " << setbase(16) << a << endl;
// affiche: Valeur de a en base 16 = C8

cout << "Valeur de a en base 10 = " << setbase(10) << a << endl;
// affiche: Valeur de a en base 10 = 200
cout << "Valeur de a en base 8 = " << setbase(8) << a << endl;

// affiche: Valeur de a en base 8 = 310

Manipulateur setw(width)

Ce manipulateur indique que les prochaines données doivent être écrites sur le nombre de caractères
indiqué, en ajoutant des caractères espaces avant.
Exemple :

int a = 11;
cout << "Valeur de a = " << setw(5) << a << endl;

25 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

Ce code affiche :

Valeur de a = 11

Manipulateur setfill(char)

Ce manipulateur modifie le caractère utilisé pour compléter les données

utilisant le manipulateur setw. Exemple :

int a = 11;
cout << "Valeur de a = " << setfill('x') << setw(5) << a << endl;

Ce code affiche :

Valeur de a = xxx11

Manipulateur setprecision(digits)

Ce manipulateur spécifie que les prochains nombres à virgule flottante doivent être écrits avec la précision
donnée. La précision donne le nombre maximum de chiffres à écrire (avant et après la virgule).

Exemple :

double f = 3.14159;
cout << setprecision (5) << f << endl;
cout << setprecision (9) << f << endl;

Ce code affiche :

3.1416
3.14159

Manipulateurs setiosflags et resetiosflags


Le manipulateur setiosflags (resp. resetiosflags) active (resp. désactive) des options de format des données.

Ces deux manipulateurs possèdent un argument dont le type est défini par l'énumération ios_base::fmtflags. Cet argument peut être :

ios_base::boolalpha

Écrire/lire les données de type bool sous forme textuelle, càd true ou false.
ios_base::oct

Écrire/lire les entiers en base octale (base 8).


ios_base::dec

Écrire/lire les entiers en base décimale (base 10).


ios_base::hex

Écrire/lire les entiers en base hexadécimale (base 16).


ios_base::showbase

Faire précéder les entiers par leur base.


ios_base::showpos

Faire précéder les nombres positifs du signe plus ( + ).


ios_base::showpoint
26 Adner Sodrel LEPHOYO
COURS PROGRAMMATION C++ I DIGL L2 2I

Toujours écrire la virgule des nombres réels.


ios_base::fixed

Écrire les nombres réels avec une virgule fixe.


ios_base::scientific

Écrire les nombres réels sous forme scientifique.


ios_base::left

Aligner les donnés à gauche (setw).


ios_base::right

Aligner les donnés à droite (setw).


ios_base::internal

Aligner les donnés en remplissant à une position interne (setw).


ios_base::skipws

Ignorer les caractères blancs avant de lire les données.


ios_base::unitbuf

Vider le buffer de sortie à chaque écriture.


ios_base::uppercase

Écrire les données en majuscules.

VI. Les pointeurs et Les références

A. Les pointeurs
1. Introduction
Une variable correspond à un emplacement en mémoire (adresse) où se trouve une valeur. Toute variable a permet
d'accéder :

à sa valeur en lecture et en écriture :

int a;
a = 10; // écriture de la valeur de a
cout << "A vaut " << a ; // lecture de la valeur de a

à son adresse en lecture seulement car l'adresse (l'emplacement mémoire) est choisie par le système :

cout << "L'adresse de A est " << &a ; // lecture de l'adresse de a

Un pointeur désigne un type particulier de variable dont la valeur est une adresse. Un pointeur permet donc
de contourner la restriction sur le choix de l'adresse d'une variable, et permet essentiellement d'utiliser la
mémoire allouée dynamiquement.
Il est utilisé lorsque l'on veut manipuler les données stockées à cette adresse. C'est donc un moyen indirect de
construire et de manipuler des données souvent très complexes.

2. Déclaration

type* identificateur;

La variable identificatrice est un pointeur vers une valeur de type type.

27 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

3. L'opérateur &
C'est l'opérateur d'indirection. Il permet d'obtenir l'adresse d'une variable, c'est-à-dire un pointeur vers cette
variable.

&identificateur // permet d'obtenir l'adresse mémoire de la variable identificateur

Il renvoie en réalité une adresse mémoire, l'adresse où est stockée physiquement la variable identificateur.

4. L'opérateur *

*variable

C'est l'opérateur de déréférencement. Il permet d'obtenir et donc de manipuler les données pointées par la variable
variable. Ainsi *pointeur permet d'accéder à la valeur pointée par pointeur en lecture et en écriture.

5. Comparaison avec une variable classique

int a;
int* pA;
pA = &a; // l'adresse de a est stockée dans pA

écriture de la valeur de a a = 10; *pA = 10;

lecture de la valeur de a cout << "A vaut " << a ; cout << "A vaut " << *pA ;

lecture de l'adresse de a cout << "L'adresse de A est " << &a ; cout << "L'adresse de A est " << pA ;

Le pointeur pA peut par la suite pointer l'adresse d'une autre variable, où bien pointer l'adresse d'un bloc de mémoire
alloué dynamiquement.

6. Exemple de programme

28 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

#include <iostream>
using namespace std;

int main()
{
int a, b, c;
int *x, *y;

a = 98;
x = &a;
c = *x + 5;
y = &b;
*y = a + 10;

cout << "La variable b vaut : " << b << endl;


cout << "La variable c vaut : " << c << endl;

return 0;
}

Exécution

La variable b vaut 108


La variable c vaut 103

Explications

Dans ce programme, on déclare 3 variables a, b et c. On déclare ensuite 2


pointeurs vers des entiers x et y. a est initialisé à 98.
x=&a; permet de mettre dans x l'adresse de a. x est désormais un pointeur vers a.
*x est la variable pointée par x, c'est-à-dire a, qui vaut donc 98 après évaluation.

c=*x+5; permet donc de transférer 98+5 donc 103 dans la variable c.

y=&b; permet de mettre dans la variable y l'adresse de la variable b. y

est désormais un pointeur vers b. a+10 vaut 98+10 donc 108.

*y=a+10; permet de transférer dans la variable pointée par y la valeur de a+10, c'est-à-dire 108. On
stocke donc 108 dans b, de manière indirecte via le pointeur y.
on affiche ensuite les valeurs de b et c c'est-à-dire respectivement 108 et 103.

7. Opérations arithmétiques sur les pointeurs


Hormis l'opérateur de déréférencement, les pointeurs peuvent être utilisés avec l'opérateur d'addition ( + ).
L'addition d'un pointeur avec une valeur entière permet d'avancer ou reculer le pointeur du nombre
d'éléments indiqué.

Exemple 1

29 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

char* ptr="Pointeur"; // ptr pointe le premier caractère de la chaîne de caractères

cout << ptr << endl; // affiche "Pointeur"

ptr = ptr+3;
cout << ptr << endl; // affiche "nteur"

cout << ++ptr << endl; // affiche "teur"


cout << --ptr << endl; // affiche "nteur"

Comme l'exemple précédent le montre, il est également possible d'utiliser les opérateurs d'incrémentation et de
décrémentation.

Exemple 2

int premiers[] = { 2, 3, 5, 7, 11, 13, 17 };


int* ptr = premiers; // pointe le premier élément du tableau

cout << hex << ptr << endl; // affiche l'adresse pointée (par exemple 01C23004)
cout << *ptr << endl; // affiche "2"
cout << *(ptr+5) << endl; // affiche "13"

ptr=&premiers[3]; // pointe le 4e élément (index 3)


cout << hex << ptr << endl; // affiche l'adresse pointée (par exemple 01C23018)
cout << *ptr << endl; // affiche "7"
cout << *(ptr-1) << endl; // affiche "5"

Dans l'exemple 2, la différence d'adresse est de 20 octets (5 * sizeof(int)). Cela montre que l'adresse
contenue dans le pointeur est toujours incrémentée ou décrémentée d'un multiple de la taille d'un élément
(sizeof *ptr).

8. Opération utilisant deux pointeurs


La seule opération valable (ayant un sens) utilisant deux pointeurs de même type est la soustraction ( - ) donnant le
nombre d'éléments entre les deux adresses. Elle n'a de sens que si les deux pointeurs pointent dans le même
tableau d'éléments.
Exemple :

char str[]="Message où rechercher des caractères.";


char *p1 = strchr(str,'a'); // recherche le caractère 'a' dans str
char *p2 = strchr(str,'è'); // recherche le caractère 'è' dans str

cout << "Nombre de caractères de 'a' à 'è' = " << (p2-p1) << endl;
// affiche : Nombre de caractères de 'a' à 'è' = 28

9. Pointeur constant et pointeur vers valeur constante


Il ne faut pas confondre un pointeur constant (qui ne peut pointer ailleurs) avec un pointeur vers une
valeur constante (l'adresse contenue dans le pointeur peut être modifiée mais pas la valeur pointée).

Dans les 2 cas le mot clé const est utilisé, mais à 2 endroits différents dans le type de la variable.

Pointeur vers une valeur constante

Le mot-clé const placé avant le type du pointeur permet d'empêcher la

modification de la valeur pointée. Exemple:


30 Adner Sodrel LEPHOYO
COURS PROGRAMMATION C++ I DIGL L2 2I

const char* msg = "essai constant";

*msg = 'E'; // <- Interdit, la valeur pointée ne peut être modifiée

msg = "Test"; // OK -> le pointeur contient l'adresse de "Test"

Pointeur constant

Le mot-clé const placé entre le type du pointeur et le nom de la variable permet d'empêcher la

modification du pointeur lui-même (l'adresse).

Exemple :

char* const msg = "essai constant";

msg = "Test"; // <- Interdit (erreur de compilation),


// ne peut pointer la chaîne "Test"

*msg = 'E'; // OK -> "Essai constant"

Pointeur constant vers une valeur constante

Le mot-clé const apparaissant aux 2 endroits empêche à la fois la modification du pointeur lui-même et celle de la
valeur pointée.
Exemple:

const char* const msg = "essai constant";

msg = "Test"; // <- Interdit (erreur de compilation),


// ne peut pointer la chaîne "Test"

*msg = 'E'; // <- Interdit, la valeur pointée ne peut être modifiée

Position du mot clé const


Une méthode simple a été proposée par Dan Sacks pour déterminer si c'est la valeur pointée ou le pointeur lui-même qui est constant.
Elle se résume en une seule phrase mais n'a jamais été intégrée dans les compilateurs :

"const s'applique toujours à ce qui le précède".

Par conséquent, une déclaration ne commencera jamais par const qui ne serait précédé de rien et qui ne pourrait donc s'appliquer à
rien. En effet, les deux déclarations suivantes sont strictement équivalentes en C++ :

const char * msg; // déclaration habituelle


char const * msg; // déclaration Sacks

Cette méthode peut s'avérer précieuse dans le cas de déclarations plus complexes :

int const ** const *ptr; // ptr est un pointeur vers un pointeur constant de pointeur d'entier constant:
// *ptr et ***ptr ne peuvent être modifiés

31 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

B. Les références
1. Présentation des références
Une référence peut être vue comme un alias d'une variable. C'est-à-dire qu'utiliser la variable, ou une
référence à cette variable est équivalent. Ce qui signifie que l'on peut modifier le contenu de la variable en
utilisant une référence.
Une référence ne peut être initialisée qu'une seule fois : à la déclaration. Toute autre affectation modifie en
fait la variable référencée. Une référence ne peut donc référencer qu'une seule variable tout au long de sa
durée de vie.

Déclaration

La déclaration d'une variable de type référence doit inclure son initialisation :

type& identificateur=variable; // Syntaxe d'initialisation des variables


// ou (strictement équivalent)
type& identificateur(variable); // Syntaxe d'initialisation des objets

Un paramètre de méthode ou de fonction de type référence est initialisé lors de l'appel à celle-ci :

type_retour nomFonctionOuMethode(type& identificateur)


{
// ...
}

// ...
nomFonctionOuMethode(variable);

Sémantique

La variable identificateur est une référence vers la variable variable. La variable variable doit être de type type.

Exemple de programme

#include <iostream>
using namespace std;

int main()
{
int a = 98,
b = 78,
c;

int &x = a;
c = x + 5; // équivaut à : c = a + 5;

int &y = b;
y = a + 10; // équivaut à : b = a + 10;

cout << "La variable b vaut : " << b << endl;


cout << "La variable c vaut : " << c << endl;

return 0;
}

32 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

Exécution

La variable b vaut : 108


La variable c vaut : 103

Explications

Dans ce programme, on définit 3 variables entières a, b et c et on initialise a à 98 et b à 78.


int &x=a; permet de déclarer une référence x vers la variable a. x+5 vaut donc
la même chose que a+5 donc 103. c=x+5; permet donc de transférer 103 dans
la variable c.
int &y=b; permet de déclarer une référence y vers la variable
b. a+10 vaut 98+10 donc 108. y=a+10; permet de transférer
108 dans la variable b.
on affiche ensuite b et c c'est-à-dire respectivement 108 et 103.

Pourquoi utiliser une référence ?

C'est la question qui peut se poser en regardant l'exemple ci-dessous, où il serait plus clair d'utiliser directement des
variables.
Les références sont principalement utilisées pour passer des paramètres aux fonctions. Voir le chapitre sur les
fonctions, section « passage de paramètres par référence ».

Les références constantes sont également utilisées pour référencer des résultats de retour de fonctions afin
d'éviter les copies. C'est particulièrement indiqué dans le cas d'objets retournés par des fonctions. Dans ce cas,
la valeur ou objet temporaire retourné a une durée de vie aussi longue que la référence.

Exemple :

class Retour
{
public:
void g() const {}
};

Retour f() { return Retour(); }

int main(int argc, char *argv[])


{
const Retour &retour = f();
retour.g();
return 0;
}

2. Les références et leur lien avec les pointeurs


Une référence est un pointeur que l'on ne peut pas réaffecter (car le compilateur l'interdit), qui se déréférence
automatiquement (à l'inverse d'un pointeur pour lequel on doit utiliser l'opérateur d'indirection), et dont à
l'inverse d'un pointeur on ne peut connaître l'adresse car le compilateur ne le permet pas. En effet, si v est une
référence alors &v donnera l'adresse de l'objet référencé par v, et non l'adresse de la case mémoire où est
stockée la référence.

33 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

3. Exercices

Exercice 1

Faites une fonction dont la déclaration sera void échanger(int & a, int & b) qui devra échanger les deux
valeurs.
Solution

void échanger(int & a, int & b)


{
int c = a;
a = b;
b = c;
}

Exercice 2

Faites une fonction pour calculer la factorielle d'un nombre. Sa déclaration sera int fact(int & n). La
fonction sera récursive et la valeur de retour sera n. En cas de problèmes, consulter l'Aide 1.
Aide 1

La factorielle (notée "!") est une fonction mathématique.


Voici quelques exemple :
4! = 4 x 3 x 2 x 1 = 24
3! = 3 x 2 x 1 = 6
2! = 2 x 1 = 2
1! = 1
Notez que :
4! = 4 x 3!
3! = 3 x 2!
2! = 2 x 1! = 2 x 1
D'où :
!n = n x !(n-1) si n > 1

Solution

Voici un exemple de fonction récursive qui ne répond pas à la consigne d'avoir une déclaration int factorielle (int &
n) et qui par conséquent ne peut être qualifiée de solution à l'exercice 2 :
#include <iostream>
using namespace std;
int factorielle(int n)
{
if(n == 1) return 1;
return n * factorielle(n-1);
}
int main(void)
{
int y = factorielle(2);
cout << "résultat : " << y << endl;
}

Une autre solution est (mais la fonction retourne factoriel n, pas n) :

34 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

#include <iostream>
using namespace std;
int factorielle(int& n)
{
if(n == 1) return 1;
n--;
return (n+1) * factorielle(n);
}
int main(void)
{
int n = 2;
int y = factorielle(n);
cout << "résultat : " << y << endl;
}

Une autre solution :

#include <iostream>

int fact(int & n)


{
if (n == 0)
{
n = 1;
return 0;
}
else if (n == 1) return 1;
else
{
int t = n-1;
int retVal = fact(t);
n = t * n;
return n/t;
}
}
main()
{
int v = 4;
int res = fact(v);
std::cout << res << " et sa factorielle :" << v << std::endl;
}

Tests

Test 1
Indiquez si la syntaxe est correcte ou non.

Cas 1

int b = n;
int & ref = b;

Cas 2

int x = 5;
int & var = x;

Cas 3
35 Adner Sodrel LEPHOYO
COURS PROGRAMMATION C++ I DIGL L2 2I

int n = 2;
int & ref = n;
if (*(ref) == 2) ref++; //ceci provoque une erreur car ref n'est pas un pointeur

Cas 4

#include <iostream>
using namespace std;
void afficher_par_reference(int & a)
{
cout << a << endl;
}

Cas 5

int b = 2;
int ref& = b;

Solution
Solutions

1. vrai
2. vrai
3. faux
4. vrai
5. faux

Test 2
Dans ces exemples, trouvez ce que le programme va afficher.

Cas 1

#include <iostream>
using namespace std;
int main()
{
int b = 2;
int a = 4;
int & ref1 = b;
int & ref2 = a;
ref2 += ref1;
ref1 -= ref2;
cout << ref2 << " " << ref1 << endl;
}

Solution

36 Adner Sodrel LEPHOYO


COURS PROGRAMMATION C++ I DIGL L2 2I

6 -4

37 Adner Sodrel LEPHOYO

Vous aimerez peut-être aussi