Debuter Avec C

Télécharger au format pdf ou txt
Télécharger au format pdf ou txt
Vous êtes sur la page 1sur 28

Chapitre II – Débuter avec C++

Historique du C et du C++

[Deitel et Deitel, Comment Programmez en C++. Eyrolles, 2ième édition, 2000, Section
1.7, pp. 9-10]

" Le C++ a été développé à partir du C qui fut lui-même élaboré à partir de deux
langages de programmation antérieurs, le BCPL et le B. Le BCPL fut développé
en 1967 par Martin Richards comme langage d’écriture de logiciels de systèmes
d’exploitation et de compilateurs. Ken Thompson modela plusieurs particularités
de son langage B à partir de leurs équivalents en BCPL. En 1970, il utilisa le
langage B pour créer des versions primitives du système d’exploitation UNIX aux
laboratoires Bell sur un ordinateur DEC PDP-7. Le BCPL et le B étaient des
langages « non typés »; chaque élément de données occupait un « mot » en
mémoire et le fardeau de traiter un élément comme un nombre entier ou comme
un nombre réel, par exemple, incombait au programmeur.

Le langage C a été développé à partir du B par Dennis Ritchie aux laboratoires


Bell et a été exécuté pour la première fois en 1972 sur un ordinateur DEC PDP-
11. Le C utilise de nombreux concepts importants des langages BCPL et B tout en
incorporant le typage des données et d’autres fonctionnalités. Le C s’est d’abord
répandu comme langage de développement du système d’exploitation UNIX et,
aujourd’hui, la majorité des systèmes d’exploitation sont écrits en C et (ou) en
C++. Au cours des deux dernières décennies, le C est en effet devenu disponible
pour la plupart des ordinateurs, car il est indépendant du matériel. En utilisant un
design soigné, il est possible d’écrire des programmes C portables sur la plupart
des machines.

À la fin des années 1970, le C avait évolué en ce qu’on désigne maintenant par les
vocables de « C conventionnel », « C classique » ou « C de Kernighan et
Ritchie ». En 1978, la publication chez Prentice-Hall du livre The C Programming
Language de Kernighan and Ritchie attira l’attention générale sur ce langage.

L’emploi très répandu du C sur différents types d’ordinateurs (parfois appelés


plates-formes matérielles) amena hélas de nombreuses variantes, similaires mais
souvent incompatibles. Cette situation était un problème sérieux pour les
développeurs chargés d’écrire des programmes portables capables de fonctionner
sur différentes plates-formes, d’où la nécessité évidente d’une version standard du
langage C. En 1983, l’American National Standards Committee on Computers
and Information Processing (X3) créait le comité technique X3J11 afin de
« fournir une définition du langage claire et indépendante de la machine ». Le
standard élaboré fut approuvé en 1989. L’ANSI travailla ensuite de pair avec
l’International Standards Organization (ISO) pour normaliser le langage C à

-1-
l’échelle mondiale; le document collectif du standard fut publié en 1990 et porte
l’identification ANSI/ISO 9899 : 1990. On peut commander des copies de ce
document auprès de l’ANSI. La deuxième édition du Kernighan et Ritchie,
publiée en 1988, est conforme à cette version appelée ANSI C.

Le C++, une extension du C, a été développé au début des années 1980 par Bjarne
Stroustrup aux laboratoires Bell. Le C++ procure une quantité de fonctionnalités
qui rehaussent le langage C et, de surcroît, offre des possibilités pour la
programmation orientée objets."

Fichiers utilisés en C++

.h Fichier d’en-tête (header). Ces fichiers textes renferment uniquement des


déclarations.

On peut inclure un fichier d’en-tête défini par le programmeur en utilisant la


directive de précompilation #include.

.cpp Fichier source. Ces fichiers textes contiennent le code des programmes.

.obj Fichier résultant de la compilation.

.lib Fichier librairie. Une librairie regroupe plusieurs fichiers « .obj ».

.exe Fichier exécutable généré par l’édition des liens.

Différences entre compilateurs

Plusieurs compilateurs ne respectent pas le standard C++ en un ou plusieurs points. Voici


quelques incompatibilités classiques.

Les fichiers d’entête système de certains vieux compilateurs possèdent une extension .h,
par exemple :

#include <iostream.h>

Ajouter simplement un .h ne fonctionne toutefois pas pour tous les fichiers inclus. Par
exemple, en C++ standard, vous pouvez inclure la directive #include <string> ; la

-2-
directive #include <string.h> n’inclut pas les chaînes C++, mais les chaînes de style C,
totalement différentes et moins utiles.

Un autre fichier d’entête courant contient des fonctions mathématiques. En C++ standard,
vous employez la directive #include <cmath>. Avec des compilateurs plus anciens, vous
employez à la place #include <math.h>.

Les anciens compilateurs ne prennent pas en charge les espaces de noms. Dans ce cas,
omettez la directive using namespace std;

Différences entre le C et le C++

Les programmes C sont des programmes C++ à quelques exceptions près.

⇒ On peut continuer à utiliser nos fonctions C dans les nouveaux


programmes C++.
⇒ Le passage au langage orienté objet s’est fait en douceur.

Différences :

- C: une variable locale doit être déclarée en tête de bloc.

C++ : cette règle ne s’applique plus.

- C++ : la déclaration d’une structure correspond obligatoirement à la


définition d’un nouveau type de donnée.

⇒ l’opérateur typedef n’est plus nécessaire.

- C++ : Un prototype de fonction doit contenir la définition des arguments


de la fonction.

C: Certains compilateurs C ne l’exigent pas.

- C: Un pointeur de type void * peut être converti implicitement lors


d’une affectation en un pointeur d’un autre type.

C++ : Ce n’est pas le cas; on peut faire appel à l’opérateur de « cast ».

- C++ : Une fonction sans valeur de retour se définit et se déclare à l’aide


du mot clé void.

C: Le mot clé void est facultatif.

-3-
Mots-clés C et C++

auto break case char const continue


default do double else enum extern
float for goto if int long
register return short signed sizeof static
struct switch typedef union unsigned void
volatile while

Mots-clés exclusifs au C++

asm bool catch class const_cast delete


dynamic_cast explicit false friend inline mutable
namespace new operator private protected public
reinterpret_cast static_cast template this throw
true try typeid typename using virtual
wchar_t

Généralités sur le C++

Aucune contrainte particulière au niveau des espaces et des sauts de ligne.


⇒ « aérer » votre programme.

La différence est faite entre les majuscules et les minuscules.

Toutes les instructions doivent se terminer par un « ; ».

Le fichier d’en-tête iostream.h est nécessaire à l’utilisation des fonctions


d’affichage à l’écran (le flux de sortie standard cout) et de saisie au clavier (le
flux d’entrée standard cin).

Ex. : #include <iostream.h>


int main()
{
int i;
cin >> i;
cout << "Premier essai :" << i;
return 0;
}

-4-
Fonctions

La bibliothèque standard du C++ offre une riche collection de fonctions pour effectuer les
calculs mathématiques courants, les manipulations de chaînes de caractères, les entrées et
les sorties, la vérification des erreurs, etc. Ces fonctions font partie intégrante de
l’environnement de programmation du C++. Leur emploi accroît la portabilité des
programmes; de plus, il est difficile d’améliorer leur performance.

La conception des programmes en C++ consistera typiquement à combiner les nouvelles


fonctions écrites par le programmeur avec des fonctions déjà disponibles dans la
bibliothèque standard. Les instructions définissant chaque fonction ne sont écrites qu’une
seule fois et sont cachées (masquées) ailleurs. Ce « masquage » des détails d’implantation
favorise une bonne conception des logiciels.

Les fonctions offrent plusieurs avantages :

- l’approche « diviser pour mieux régner » améliore la maniabilité du


développement du programme
- la réutilisation du logiciel : l’utilisation de fonctions existantes comme blocs de
construction pour créer de nouveaux programmes.
- éviter la répétition du code dans un programme par simple appel à la fonction.

Chaque fonction devrait se limiter à effectuer une tâche unique et bien définie. Le nom de
la fonction devrait exprimer cette tâche avec clarté.

Considérons maintenant la définition d’une fonction (syntaxe) :

type_de_valeur_renvoyée Nom_de_la_fonction
( type_de_x argument_x, ……
type_de_z argument_z)
{ déclarations et instructions formant le corps de la fonction (bloc) }

type_de_ valeur_renvoyée correspond à :

- un type standard de C++


- un type de donnée créé par le programmeur
- une structure
- une classe
- void (si la fonction ne renvoie rien)

Note : (i) C++ n’impose pas de récupérer la valeur retournée par une fonction.
(ii) Si la fonction ne possède pas de paramètre, C++ n’indique rien entre les
parenthèses ou le mot clé void. Une liste de paramètres de fonction vide en

-5-
C++ a une signification tout à fait différente d’une telle liste en C. En C,
elle signifie que la vérification des arguments est désactivée; autrement
dit, l’appel de fonction peut passer les arguments qu’il veut. En C++, elle
signifie que la fonction ne prend aucun argument. Les programmes en C
utilisant cette caractéristique pourraient donc signaler des erreurs de
syntaxe lors de leur compilation en C++.

Exemple :

#include <iostream.h>

// Définition de la fonction

int cube(int m)
{
return m * m * m;
}

int main()
{
for (int i = 1; i <= 10; i++)
cout << cube(i) << “ “;
cout << endl;
return 0;
}

Définition d’un prototype de fonction

L’une des caractéristiques les plus importantes du C++ est le prototype de fonction. Cela
indique au compilateur le nom de la fonction, le type de données renvoyé par la fonction,
le nombre de paramètres que la fonction s’attend à recevoir, les types des paramètres et
l’ordre dans lequel ces paramètres devraient se suivre. Le compilateur utilise les
prototypes de fonctions pour valider les appels de fonction. Ils sont requis en C++; vous
pouvez utiliser les directives de précompilateur #include pour obtenir des prototypes de
fonctions de la bibliothèque standard ou des prototypes de fonctions personnelles à partir
de fichiers d’en-tête. Comme nous l’avons vu précédemment, un prototype de fonction
n’est pas requis si la définition de la fonction apparaît avant la première utilisation de
cette fonction; le cas échéant, la définition de la fonction sert également de prototype de
fonction.

Exemple :

#include <iostream.h>

int cube(int n); // Prototype de la fonction

-6-
int main()
{
for (int i = 1; i <= 10; i++)
cout << cube(i) << “ “;
cout << endl;
return 0;
}

// Définition de la fonction

int cube(int m)
{
return m * m * m;
}

En plus de fournir au compilateur les caractéristiques d’une fonction, cela donne aussi
l’occasion de donner la spécification d’une fonction.

Exemple :
float Racine_carree(float x);

/* Permet de calculer la racine carrée d’un nombre réel.


Pré - x > 0
Post - Retourne la racine carrée de la valeur du paramètre x. */

Note : Même si le compilateur l’ignore, il est préférable d’utiliser des noms de


paramètres dans les prototypes de fonctions à des fins de documentation.

Fichiers d’en-tête

Contiennent notamment les prototypes des fonctions standards que vous


appelez.

Permettent de séparer la spécification d’un type de donnée ou d’une classe de


son implantation.

Ils ne contiennent pas le corps des fonctions, uniquement des


déclarations.

Renferment :
- d’autres fichiers d’en-tête,
- les prototypes des fonctions
- la déclaration de variables externes

-7-
- des types de données
- les déclarations de structures,
- les déclarations de classes.

Précompilateur C++

L’une des directives de précompilation est :

#include <nom_du_fichier.h> permet d’inclure un fichier d’en-tête

i.e. remplace cette ligne par le contenu du fichier.

Le fichier est recherché dans les répertoires par défaut.

Ex. : #include <iostream.h>

indique au précompilateur d’inclure le contenu du fichier


d’en-tête du flux d’entrée /sortie iostream.h dans le
programme.

Ce fichier doit être inclus dans tout programme qui dirige


des données de sortie vers l’écran ou qui reçoit des données
d’entrée à partir du clavier au moyen d’un flux
d’entrée/sortie de style C++.

Pour inclure nos propres fichiers d’en-tête, la syntaxe est :

Ex. : #include "Polygone_2D.h"

Le fichier d’en-tête doit être dans le même répertoire que le


fichier source qui a utilisé la commande include.

Note : (1) Il ne faut pas inclure le même fichier plusieurs fois.

(2) Ces techniques permettent de définir une architecture de projets qui


correspond souvent à la complexité des développements.

-8-
Vehicule

Velo Bateau_a_vapeur Automobile

Programme
d’application

Un exécutable, construit à partir de plusieurs fichiers sources, doit respecter


les règles suivantes :

- un et un seul des fichiers doit contenir la fonction main()


- chaque fichier du projet « .cpp » doit être compilé séparément, dans
l’ordre du projet (règle de prédéclaration respectée).

-9-
Diriger le flux de données vers l’objet du flux de sortie standard
cout normalement associé à l’écran

Ex. : cout << "Bienvenue au C++!\n";


où - << désigne l’opérateur d’insertion de flux,
- la valeur à droite de l’opérateur est insérée dans le flux de sortie
lors de l’exécution du programme,
- normalement, les caractères de l’opérande droit s’affichent
exactement comme ils apparaissent entre les guillemets, sauf s’il
s’agit de caractères qui ne sont pas affichables. La barre oblique
inverse (\) désigne un caractère de changement de code et indique
de sortir un caractère spécial.

Séquence de Description
changement de code
\n Nouvelle ligne. Positionne le curseur au début de la
ligne suivante.
\t Tabulation horizontale. Déplace le curseur à la
tabulation suivante.
\r Retour de chariot. Positionne le curseur au début de la
ligne courante; ne passe pas à la ligne suivante.
\a Alerte. Active la sonnerie du système.
\\ Barre oblique inverse. Utilisée pour afficher un
caractère de barre oblique inverse.
\" Guillemets. Sert à afficher un caractère de guillemets.

Diriger le flux de données vers l’objet du flux d’entrée standard


cin normalement associé au clavier

Exemple : int entier1, entier2;


cout << "Entrez le premier entier\n";
cin >> entier1;
cout << "Entrez le second entier\n";
cin >> entier2;
cout << "La somme est " entier1 + entier2 << endl;

endl est un manipulateur de flux; il produit une nouvelle ligne, puis vide le tampon de
sortie. endl force donc toute sortie accumulée à s’afficher immédiatement.

Dans l’exemple précédent, on illustre aussi l’emploi de multiples opérateurs << dans une
seule instruction. La concaténation de flux est possible; il n’est pas nécessaire d’utiliser
des instructions de sortie multiples pour produire plusieurs parties de données.

- 10 -
Variables et opérateurs

Les déclarations de variables peuvent être placées à peu près n’importe où


dans une fonction mais doivent apparaître avant que la variable ne soit utilisée
dans le programme.

float pi = 3.14159;
int jour = 21, mois = 9, annee;

Nommage des variables

- N’importe quelle série de caractères formée de lettres, de chiffres ou de


caractères de soulignement ( _ ) mais ne commençant pas par un chiffre.
- Pas de norme, adopter une convention et s’y tenir.
- Les mots réservés du C++ ne peuvent pas être utilisés comme noms de
variables.
- Le C++ interprète différemment les majuscules et les minuscules.
- Utiliser des noms de variables évocateurs pour rendre votre programme
plus facile à lire.
- Éviter d’utiliser des identificateurs commençant par des caractères de
soulignement car le compilateur C++ utilise parfois des noms de ce genre
pour ses propres tâches internes.

Type de variable Famille Taille Intervalle de valeurs


char Caractère 8 bits -128 à 127
unsigned char Octet 8 bits 0 à 255
short int Entier 16 bits -32 768 à 32 767
unsigned short int Entier 16 bits 0 à 65 535
int Entier 16 bits -32 768 à 32 767
unsigned int Entier 16 bits 0 à 65 535
long int Entier 32 bits -2 147 483 648 à 2 147 483 647
unsigned long int Entier 32 bits 0 à 4 294 967 295
float Réel 32 bits 3.4 e -38 à 3.4e+38
double Réel 64 bits 1.7e-308 à 1.7e+308
long double Réel 80 bits 3.4e-4932 à 1.1e+4932
bool Booléen 8 bits false ou true

Ex. : bool Etat;


Etat = false;
…..
cout << "Vaut 0 ou 1" << Etat;
// Le type bool est souvent utilisé : bool Pile_pleine();

- 11 -
Opérateur sizeof :

Permet d’obtenir l’espace occupé par une variable ou un type de donnée (octet).

Ex. : short i = 2;
cout << sizeof(i);
cout << sizeof(long);

Portée des identificateurs et mémorisation

La portée d’un identificateur est l’endroit où l’on peut référencer l’identificateur dans un
programme. Certains identificateurs peuvent être référencés dans la totalité d’un
programme; d’autres ne peuvent l’être que dans certaines parties d’un programme. Un
autre attribut associé à un identificateur est la période durant laquelle l’identificateur
existe en mémoire.

Variables locales

Ces variables sont créées lors de l’entrée du bloc dans lequel elles sont déclarées et
détruites lors de la sortie du bloc. Ces variables sont utilisables dans la portée où elles
sont définies i.e. à l’intérieur du bloc ou de la fonction où elles sont déclarées; elles sont
détruites lorsqu’on sort de sa portée.

En C++, contrairement à C, ce n’est plus nécessaire de définir les variables locales en tête
de bloc.

Ex. : #include <iostream.h>


int cube(int n); // Prototype de la fonction
void main()
{
for (int i = 1; i <= 10; i++)
{
int j;
cout << cube(i) << “ “;
j = i;
};
cout << i << endl;
// cout << j << endl; n’est pas autorisée.
}

int cube(int m)
{
return m * m * m;
}

- 12 -
Note : Le mot-clé auto déclare explicitement des variables locales qui n’existent que
dans le corps de la fonction dans laquelle la définition apparaît. Les variables
locales ont, par défaut, cette propriété; c’est pourquoi, le mot clé auto est
rarement utilisé.

Arguments de fonction :

- cas particulier de variables locales où la portée est toujours le corps de la


fonction.
- Seule différence : elles sont initialisées de manière différente à chaque
appel de la fonction.

Variable registre :

- indique au compilateur que cette variable locale ou paramètre de fonction


doit être de préférence stockée dans un registre haute vitesse de la
machine, s’il en existe un, disponible.

- Permet d’améliorer la performance en éliminant la surcharge causée par le


stockage répétitif des variables de la mémoire vers les registres et le
chargement correspondant des résultats en mémoire.

Ex . : register int compteur = 1;

Variable statique :

- Les variables locales déclarées avec le mot-clé static ne sont encore


connues qu’à l’intérieur de la fonction dans laquelle elles sont définies,
mais, elles conservent leurs valeurs lors de la sortie de la fonction.

- Elle n’est pas détruite lorsque le programme quitte la fonction.

- Elle conserve sa valeur entre les différents appels de cette fonction.

- Si vous ne les initialisez pas, elles prendront la valeur 0.

- Peut être utilisé pour connaître le nombre de fois où vous appelez une
fonction (compteur de fonction).

Ex. : static int compteur = 1;

Variables globales

- définies en dehors des fonctions

- 13 -
- portée : le fichier source où elles sont déclarées.

- Les variables globales conservent leurs valeurs pendant toute l’exécution


du programme.

- Seules les fonctions qui sont définies après cette variable pourront
l’utiliser.

- La mise à jour des variables globales doit se faire avec des méthodes bien
spécifiées.

Ex. : #include <iostream.h>

int total = 0; // Variable globale

int cube(int n); // Prototype de la fonction


void main()
{
for (int i = 1; i <= 10; i++)
{
total = total + cube(i);
};
cout << total << endl;
}

int cube(int m)
{
return m * m * m;
}

Variables et fonctions externes :

- Par défaut, les variables globales et les noms de fonctions ont l’attribut extern.

- Manipulables par toutes les fonctions définies dans le même fichier source à la suite
de leurs déclarations ou définitions dans le fichier.

- Puisqu’un projet peut contenir plusieurs sources, le mot clé extern est utilisé quand
une même variable est partagée par plusieurs fichiers d’un même projet.

- À éviter car le fichier source qui renferme la définition de la variable devrait


contenir toutes les fonctions qui manipulent cette variable.

- Préférable d’opter pour un bon découpage, les fichiers d’en-tête et la commande


#include.

- 14 -
Ex. : float x; fichier gestion.cpp
extern float x; fichier Application_gestion.cpp

Conversion de types

Consiste à modifier le type de données stockées dans une variable.

- Conversion implicite

La conversion est traitée automatiquement par le compilateur.

Ex. : char c = 65;


int i = 'A';

- Conversion explicite

La conversion est forcée en utilisant l’opérateur de cast i.e. il s’agit


d’indiquer entre parenthèses le type pour lequel vous souhaitez
transformer une variable.

Ex. : float f1 = 10.4;


float f2 = (long int) f1;

Le C++ offer l’opérateur de forçage de type unaire

static_cast<type de donnée> (opérande)

pour effectuer une conversion explicite.

Ex. : float moyenne;


int total, compteur;
……..
moyenne = static_cast<float>(total) / compteur;

Dans cet exemple, le résultat du calcul de total / compteur donne un entier


puisque les 2 variables sont entières et la partie fractionnaire est perdue.
L’opérateur de forçage crée une copie temporaire à virgule flottante de son
opérande entre parenthèses, total. La valeur stockée dans total est toujours
un entier. Le calcul est maintenant constitué d’une valeur à virgule
flottante divisée par un entier. Puisque le compilateur C++ ne peut
qu’évaluer des expressions dans lesquelles les types de données des
opérandes sont identiques, le compilateur effectue une conversion
implicite; dans l’exemple, une conversion implicite à float de compteur est
effectuée.

- 15 -
Définition des constantes symboliques

(1) Utiliser le préprocesseur

#define PI = 3.14159;

PI peut être utilisé partout dans le programme; PI ne possède pas de type.

***** à éviter ****


(2) le mot clé const

const float PI = 3.14159;

données typées

(3) utilisation des énumérations

Le mot clé enum permet de définir une suite de constantes numérotées


automatiquement par le compilateur
(la 1e constante prend la valeur 0,
la 2e constante prend la valeur 1, etc.)

ex. : enum Direction{NORD, SUD, EST, OUEST};

0 1 2 3

On peut imposer des valeurs :

Enum Direction{ NORD = 9, SUD = 12, EST = 5, OUEST = 2};

Ces constantes sont considérées comme des entiers (int) par le compilateur.

Structures de contrôle

Le C++ n’a que sept types différents de structures de contrôle : la séquence, 3 types de
sélection (if, if-else, switch) et 3 types de répétition (while, do / while, for), combinées de
seulement deux façons : par empilement ou par imbrication des structures de contrôle.

*** À réviser si nécessaire ***

- 16 -
Tableaux

Rappel :

- permet de définir un ensemble de variables de même type

- le nombre de composantes doit être spécifié par une constante et jamais


par une variable

- on ne peut pas modifier leur taille après les avoir créés

- ce sont des tableaux statiques par opposition à des tableaux dynamiques


où l’allocation mémoire est réalisée à l’exécution du programme et une
variable peut être utilisée pour indiquer le nombre d’éléments.

Ex. :

const int Max_elements = 10;

float tache[Max_elements];

int b[100], x[27];


ième
- les indices des tableaux commencent à 0. Le i élément du tableau est
d’indice i – 1.

- Les crochets utilisés pour l’indice d’un tableau sont en réalité un opérateur
du C++.

- L’initialisation des éléments d’un tableau peut se faire de différentes


façons :

Ex. : int n[10] = {0}; // les éléments restants autres que n[0] sont
// initialisés à 0 par défaut.

int m[] = { 1, 2, 3, 4, 5}; // Crée un tableau à 5 éléments.

int o[3] = {-1, 0, 1}; // Crée un tableau à 3 éléments.

- On peut appliquer la spécification static à une déclaration locale de


tableau pour éviter que ce tableau ne soit créé et initialisé à chaque appel
de la fonction. Cette mesure évite aussi la destruction du tableau à chaque
sortie de la fonction et améliore la performance du programme. Le tableau
existera donc pour la durée complète du programme mais n’est visible que
dans le corps de cette fonction.

- 17 -
Les tableaux déclarés static sont initialisés lors du chargement du
programme. Si le programmeur n’initialise pas explicitement un tableau de
type static, le compilateur l’initialisera à zéro. Toutefois, les éléments du
tableau local de type static d’une fonction ne sont pas initialisés à chaque
appel de la fonction; ils conservent leurs anciennes valeurs.

Ex. : void Exemple_de_fonction(void)


{
static int tableau[10];
...
}

Passage d’un tableau à une fonction :

Voici un exemple :

void modifierTableau(int Tab[], int taille) ;


int main()
{
int tableau[5] = {0} ;
...
modifierTableau(tableau, 5) ;
...
}

...

Il peut arriver qu’une fonction ne doivent pas être autorisée à modifier les éléments d’un
tableau dans vos programmes. Le C++ offre le qualificatif de type const pour y arriver.
En effet, lorsque le qualificatif const précède le paramètre d’un tableau, les éléments de
ce dernier deviennent constants dans le corps de la fonction courante.

Ex. : void essai(const int tab[]) ;

int main()
{
int a[] = {10, 20, 30};
essai(a) ;
...
}
void essai(const int b[])
{
b[0] = 2 ; // erreur
...
}

- 18 -
Chaînes de caractères

Le type chaîne de caractères n’existe pas en C++ à moins que …..

Pour créer une chaîne, on doit définir un tableau de caractères.

Ex. : char Nom[20+1]; // 20 éléments + 1 caractère de fin de chaîne

La longueur que vous indiquez à la création d’une chaîne est le nombre


maximum de caractères de la chaîne.

On n’est jamais obligé d’utiliser toute la capacité de la chaîne. Le cas


échéant, la chaîne n’est pas complétée par des espaces.

Il existe un caractère \0 (code ASCII 0) pour indiquer la fin de la chaîne.


Ce caractère est essentiel pour éviter les débordements.

Il intervient dans les cas suivants :

- il faut prévoir l’espace occupé par ce caractère lors de la création de la


chaîne.

- en initialisant une chaîne, caractère par caractère, vous devez placer le


caractère \0.

- si l’on souhaite tronquer ou vider une chaîne.

Initialisation d’une chaîne

(A) caractère par caractère

Nom[0] = 'O'; Nom[1] = 'U'; Nom[2] = 'I';

Nom[3] = '\0'; // ou Nom[3] = 0;

cout << Nom; // C’est le nom du tableau qui est spécifié.

Note : Chaque fois que vous avez besoin de passer une chaîne de caractères à une
fonction, vous devez passer le nom du tableau.

- 19 -
(B) utilisation des fonctions de manipulation de chaînes

les prototypes des fonctions de manipulation de chaînes (40 environ) se trouvent


dans le fichier d’en-tête string.h

#include <iostream.h>
#include <string.h>
int main()
{
char Nom[20+1];
char Prenom[20+1];

strcpy(Nom, "Boivin"); // Initialisation de Nom


strcpy(Prenom, "Virginie"); // Initialisation de Prenom

strcat(Nom, " - Leduc"); // Concaténation d’une chaîne à Nom

cout << Prenom << " " << Nom;


return 0;
}

Vider une chaîne

- initialiser le 1er élément du tableau à \0.

- strcpy(Nom, ""); // Copie d’une chaîne vide.

Tronquer une chaîne

Nom[7] = "\0";
cout << Nom; // Donne à l’affichage Boivin

Nom[7] = "-";
cout << Nom; // Donne à l’affichage Boivin – Leduc
// L’usage de \0 n’écrase pas les caractères suivants.

Comparer 2 chaînes

Ex. : strcmp(Prenom, " - ");

Retourne la valeur 0 si les 2 chaînes sont identiques.

- 20 -
Structures (enregistrement)

Une structure est un type de donnée permettant de regrouper plusieurs variables qui
peuvent être de types différents.

struct Client
{
char Nom[20+1];
char Prenom[20+1];
int Matricule;
float Remuneration_totale;
};
……..
Client X; Client * pX;
……..
X.Matricule = 7834; // Accès au champ Matricule via l’opérateur point ( . )
pX = &X;
cout << pX -> Matricule; // Accès au champ Matricule via un pointeur vers l’objet
// grâce à l’opérateur flèche ( -> ).
// L’expression pX -> Matricule équivaut à (*pX).Matricule.

Toutefois, une structure ne peut contenir aucune instance d’elle-même. Par exemple, un
membre de type Client ne peut jamais être déclaré au sein de la définition de la structure
Client. Par contre, un pointeur vers une autre structure Client peut y être inclus.

Une fonction peut récupérer ou renvoyer une variable de type structure.

Ex. : void Affiche(const Client & X)


{
….
}

Cet exemple permet le passage par référence des structures Client vers la fonction
d’affichage éliminant la surcharge associée au passage par valeurs qui aurait nécessité des
copies des structures pour les passer à la fonction d’affichage. De plus, l’utilisation de
const empêche la modification de la structure Client par la fonction d’affichage.

En C, les structures ne peuvent être affichées de façon globale; on doit plutôt mettre en
forme et afficher leurs membres l’un après l’autre. On peut écrire une fonction affichant
tous les membres d’une structure selon un format approprié quelconque. La surcharge de
l’opérateur << permet un affichage facile de variables d’un type structure.

- 21 -
Pointeurs

Incontournable en C++. Variable qui contient l’adresse d’une autre


variable. &a désigne l’adresse de la variable a.

déclaration de pointeurs : type * nom_du_pointeur

Ex. : char * p;

Client * Q;

Q = &X; Types primaires ou créés par l’utilisateur


(typedef, enum, struct, class)

Un pointeur doit obligatoirement être typé.

char c;
……
p = &c;
…….
*p = "a"; // L’emploi de l’opérateur * de cette façon est ce
// qu’on appelle la déréférenciation d’un pointeur.

Constante d’initialisation des pointeurs : NULL ou 0.

Avantage des pointeurs : permet l’accès à des variables hors de portée.

Utilisation des pointeurs avec les fonctions


Permet d’accéder à une variable déclarée dans une autre fonction, i.e.
quelle que soit sa portée.

Ex. : #include <iostream.h>


void Carre(float * Nombre)
{
float X = *Nombre;
*Nombre = X * (*Nombre); // *Nombre=(*Nombre) * (*Nombre);
}
void main()
{
float Y = 10.9;
Carre(&Y);
cout << Y;
}

- 22 -
Une fonction peut aussi renvoyer l’adresse d’une variable.

float * Carre(float * Nombre)


{
// On ne doit pas retrouver l’adresse d’une variable locale.
}

Arithmétique des pointeurs

Quand vous ajoutez la valeur 1 au contenu d’un pointeur, celui-ci ne se


déplace pas nécessairement d’un octet en mémoire, mais plutôt du nombre
d’octets équivalent à la taille de la variable pointée.

Utilisée avec les tableaux.

Définition d’un pointeur vers un tableau

(i) déclarer un pointeur vers le type correspondant au type des composantes du


tableau

(ii) initialiser ce pointeur avec l’adresse du 1er élément du tableau

(iii) utiliser l’arithmétique des pointeurs pour accéder aux éléments du tableau.

int Mois[12];
int * pEntier;

pEntier = &Mois[0]; // ou encore, pEntier = Mois;

….

*pEntier = 1; // initialiser la 1e composante


*(pEntier+1) = 2; // initialiser la 2e composante
*(pEntier+2) = 3; // initialiser la 3e composante
etc.
Note : Le nom d’un tableau correspond à un pointeur constant (qui ne peut être modifié),
i.e. l’adresse du 1e élément de ce tableau

La notation pointeur peut être utilisée pour désigner les éléments du tableau ou
utiliser la notation tableau avec un pointeur.

- 23 -
int Mois[12];
int * pEntier;
pEntier = Mois;

….

*Mois = 1; // ou encore, pEntier[0] = 1;


*(Mois + 1) = 2; // ou encore, pEntier[1] = 2;

Passage d’un tableau à une fonction à l’aide de pointeurs

Il faut envoyer l’adresse de ce tableau et non le tableau.

Ex. I : int Somme(int * pEntier, int Nb_elements)


{
int S = 0;
for (int i = 0; i < Nb_elements; i++) S = S + *(pEntier + i);
return S;
}

cout << Somme(Mois, 12);

Ex. II : char Titre[10];


void Afficher(char * chaine)
{
cout << chaine << "\n";

}
….
Afficher("POO");
….
Afficher(Titre);

Ex. III : void modifierTableau(int Tab[], int taille) ;


int main()
{
int tableau[5] = {0} ;
...
modifierTableau(tableau, 5) ;
...
}

...

- 24 -
Quatre méthodes permettent de passer un pointeur à une fonction :

(A) un pointeur non constant vers des données non constantes

Cela n’inclut pas const.

Ex. : void Conversion(char * c);


int main()
{
char chaine[] = "conversion en majuscules";
Conversion(chaine);
cout << chaine << endl;
}

void Conversion(char * c)
{
// On peut modifier c et la chaîne pointée par c.
}

(B) un pointeur non constant vers des données constantes

Ex. : void Affichage(const char *);

int main()
{
char chaine[] = "affichage des caracteres";
...
Affichage(chaine);
...
}

void Affichage(const char * sPtr)


{
for (; *sPtr != ‘\0’; sPtr++) cout << *sPtr;
}

(C) un pointeur constant vers des données non constantes

Cela pointe toujours vers le même emplacement mémoire et peut modifier les données
contenues à cet emplacement. Ceci est le cas par défaut pour un nom de tableau qui est en
fait un pointeur constant au début du tableau. On peut accéder à toutes les données du
tableau et les modifier en utilisant le nom et les indices du tableau.

- 25 -
Ex. : int x, y;
int * const ptr = &x;
*ptr = 7;
ptr = &y; // erreur

(D) un pointeur constant vers des données constantes

Ex. : void essai(const int tab[]) ;

int main()
{
int a[] = {10, 20, 30};
essai(a) ;
...
}
void essai(const int b[])
{
...
}

Pointeurs et structures

#include <iostream.h>
#include <string.h>
struct Client
{
char Nom[20+1];
char Prenom[20+1];
int code;
};
void Afficher(Client * pC)
{
cout << pC -> Nom;
cout << pC -> Prenom;
cout << (*pC).code;
}
int main()
{
Client C;
strcpy(C.Nom, "BEAUDOIN");
strcpy(C.Prenom, "LUC");
C.code = 1234;
Afficher(&C);
Return 0;
}

- 26 -
Manipulation de pointeurs de fonctions

- Un pointeur peut être déclaré dans le but de contenir l’adresse d’une fonction.

- Cela permet d’appeler la fonction exactement de la même manière qu’avec son nom.

- Le type de fonction acceptée doit être indiqué.

Ex. : int (*p) (int * pEntier, int N); // Déclaration du pointeur p de fonction.

p = Somme; // Initialisation du pointeur de fonction p.

cout << p(Mois, 12); // Appel de la fonction Somme.

Note :

(i) L’adresse d’une fonction correspond à son nom.

(ii) Les pointeurs de fonction peuvent être utilisés par exemple dans le cas d’un outil
de gestion de menus.

Allocation dynamique de la mémoire

Les opérateurs new et delete fournissent un moyen plus commode d’effectuer les tâches
d’allocation dynamique de la mémoire que les appels de fonctions malloc et free du
langage C.

Soit

Personne * ptrPersonne;

en utilisant la version ANSI du C, nous pouvons créer dynamiquement une variable de


type Personne :

ptrPersonne = malloc (sizeof (Personne) );

cela nécessite un appel à malloc et l’utilisation explicite de l’opérateur sizeof. Dans


certains cas, on devait recourir au forçage de type sur le pointeur renvoyé par malloc
(Personne *). Finalement, la fonction malloc ne fournit aucune méthode d’initialisation
pour le bloc de mémoire allouée.

- 27 -
En C++, on a :

ptrPersonne = new Personne;

une variable de taille appropriée est automatiquement créée et l’opérateur new retourne
un pointeur de type correspondant.

Pour libérer l’espace occupé, vous devez utiliser l’opérateur delete :

delete ptrPersonne;

Le C++ permet de fournir un initialisateur pour une variable nouvellement créée comme,
par exemple,
float * ptrValeur = new float(3.14159);

Pour créer un tableau d’entiers à 10 éléments, on a :

int * ptrTableau = new int[10];

et pour le détruire, on a :

delete [] ptrTableau;

Note :

(i) Le mélange des allocations dynamiques de mémoire de style new et delete


avec celles du genre malloc et free est une erreur de logique; l’espace créé par
malloc ne peut être libéré par delete et les objets créés par new ne peuvent être
supprimés par free.
(ii) L’espace créé pour un tableau doit être supprimé avec l’opérateur delete [] et
l’espace créé pour un élément individuel doit être supprimé avec l’opérateur
delete.
(iii) Il est préférable de n’utiliser que new et delete.
------------------------------------

- 28 -

Vous aimerez peut-être aussi