• Aucun résultat trouvé

2. INTRODUCTION AU LANGAGE C++ .............................................................................. 3 2.1. Historique ...............................................................................................................................

N/A
N/A
Protected

Academic year: 2022

Partager "2. INTRODUCTION AU LANGAGE C++ .............................................................................. 3 2.1. Historique ..............................................................................................................................."

Copied!
27
0
0

Texte intégral

(1)

2. INTRODUCTION AU LANGAGE C++ ... 3

2.1. Historique ... 3

2.2. Implantation de modules en C++... 3

2.3. Structure d’un programme C++... 3

2.4. Commentaires ... 3

2.5. Types de base... 3

2.6. Définition de variables... 4

2.7. Les constantes... 4

2.8. Opérateurs et expressions ... 4

2.8.1 Opérateurs arithmétiques... 4

2.8.2. Opérateurs relationnels... 4

2.8.3. Opérateurs logiques... 4

2.8.4. Affectation ordinaire ... 5

2.8.5. Opérateurs d’incrémentation / décrémentation... 5

2.8.6. Opérateurs d’affectation élargie... 5

2.8.7. Opérateurs de décalage et logique bit à bit ... 5

2.8.8. Priorité des opérateurs... 6

2.9. Les instructions de contrôle ... 6

2.9.1. Notion de bloc ... 6

2.9.2. Instruction if... 6

2.9.3. Instruction switch ... 7

2.9.4. do … while... 7

2.9.5. while... 8

2.9.6. for... 8

2.10. Les fonctions ... 8

2.10.1. Déclaration ... 8

2.10.2. Définition... 8

2.10.3. Appel d’une fonction ... 9

2.10.4. Variables locales et variables globales ... 9

2.10.5. Passage de paramètres ... 10

2.10.6. Fonction inline... 11

2.10.7. Surcharge de fonction ... 11

2.10.8. Type du résultat d’une fonction ... 11

2.10.9. Exemple ... 12

2.11. Les tableaux et les pointeurs ... 13

2.11.1. Les tableaux à un indice... 13

2.11.2. Les tableaux à plusieurs indices ... 14

2.11.3. Notion de pointeur – les opérateurs * et &... 14

2.11.4. Un nom de tableau est un pointeur constant... 17

2.11.5. Les tableaux transmis en argument ... 17

2.12. Les références ... 18

2.13. Allocation dynamique de mémoire... 19

Version C... 19

Version C++ ... 19

Exemple d’utilisation des pointeurs : ... 20

2.14. Les chaînes de caractères ... 21

2.14.1. Représentation ... 21

2.14.2. Généralités sur les fonctions portant sur des chaînes... 22

(2)

2.15.2. Entrées/sorties en C... 22

2.16. Les fichiers... 22

2.16.1. Les fichiers en C... 22

2.16.2. Quelques bases sur les fichiers en C++ ... 24

2.17. Espaces de noms... 25

2.17.1. Définition... 25

2.17.2. Utilisation ... 25

2.18. Directives de prétraitement... 26

2.18.1. Forme d’une directive ... 26

2.18.2. #define... 26

2.18.3. #undef... 26

2.18.4. #include... 26

2.18.5. Compilation conditionnelle... 26

2.19. Structure ... 27

2.19.1. Déclaration ... 27

2.19.2. Accès aux membres ... 27

2.19.3. Utilisation ... 27

(3)

C C++

2. INTRODUCTION AU LANGAGE C++

2.1. Historique

C (pour Unix) C++ (Bjane Stroustrup) (pg° procédurale) (pg° orientée objet)

2.2. Implantation de modules en C++

1 pgm simple 1 fichier interface (spécification) .h

1 fichier d’implémentation .c ou .cpp 2.3. Structure d’un programme C++

1 programme simple main() Ex de pgm :

int main() entête {

; Bloc d’instructions (entre {})

}

Une fonction ne peut jamais contenir d’autres fonctions.

2.4. Commentaires /*_____

______

______ */

int main() // _____

{…

2.5. Types de base

char caractères int entiers float réels

double réels grande précision

void type « rien » (fonction qui ne retourne rien procédure) bool booléens

(4)

2.6. Définition de variables int main()

{ // transtypage : ex : (int) C

int A,B ; // 9 C sera vu comme int double C ;

B=A ; C=A+B ; }

2.7. Les constantes

const int A=2 ; 2.8. Opérateurs et expressions

2.8.1 Opérateurs arithmétiques

+, -, *, /, % (modulo), - (- unaire) char D=’b’ ;

D=D+1 ; // code ASCII suivant => c 2.8.2. Opérateurs relationnels

== identique

!= différent

<

>

<=

>=

if (A==B) {

… }

A = (B==2)*4 ;

booléen =1 si VRAI =0 si FAUX 2.8.3. Opérateurs logiques

et &&

ou ||

non !

if (A<1 && B>3) {

… }

(5)

2.8.4. Affectation ordinaire

=

ex : A=B+3 ; B+C=A ;

2.8.5. Opérateurs d’incrémentation / décrémentation i=i+1 ; i++ ;

++i ; int i=3, n ;

n=++i-3 ; // n vaut 1, i vaut 4 n=i++-3 ; // i vaut 4, n vaut 0 2.8.6. Opérateurs d’affectation élargie

i=i+2 ; i+=2 ;

+= -= *= /= %=

A=2*A ; A*=3 ;

2.8.7. Opérateurs de décalage et logique bit à bit

<< décalage

A=A<<1 ; // si A=0010110101

// alors => 0101101010 (tjs 0 en fin)

<<1 *21

>>

>>1 /21

~ complément à 1

& ET logique

| OU logique

^ OU EXCLUSIF

int A=9, B=8, C ; A … 1001

B … 1000 C … 1000 C=A&B ;

(6)

2.8.8. Priorité des opérateurs

Catégorie d’opérateurs Opérateurs Assoc.

Fonction, tableau, membre de structure,

pointeur sur un membre de structure () [] . -> G=>D

Opérateurs unaires - ++ -- !

~ * & sizeof (type) D=>G

Multiplication, division, modulo * / % G=>D

Addition, soustraction + - G=>D

Opérateurs binaires de décalage << >> G=>D Opérateurs relationnels < <= >= > G=>D

Opérateurs de comparaison == != G=>D

Et binaire & G=>D

Ou exclusif binaire ^ G=>D

Ou binaire | G=>D

Et logique && G=>D

Ou logique || G=>D

Opérateur conditionnel ?: D=>G

Opérateurs d’affectation = += -= *= /= %=

&= ^= |= <<= >>= D=>G

Opérateur virgule , G=>D

2.9. Les instructions de contrôle 2.9.1. Notion de bloc

C’est une suite d’instructions délimitée par { … }.

Toutes les instructions simples se terminent par ; 2.9.2. Instruction if

if (Condition) // parenthèses obligatoires {

____

____

}

else // facultatif (lorsque rien à faire) {

____

____

}

Un else se rapporte toujours au dernier if rencontré auquel un else n’a pas encore été attribué.

(7)

if (i==0)

if (A>10) {

… }

else // correspond au 2ème if (malgré la mise en page) {

… }

-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_

if (i==0) {

if (A>10) {

… }

}

else // correspond au 1er if {

… }

2.9.3. Instruction switch int i=2 ;

switch (i) // switch (qqch) où qqch est un entier

{ // ou un caractère

case 0 : ____

____

break ; // si pas de break, ça case 2 : ____ // continue (ici, si pas de break ; // break, ça exécute case 2) case 1 : ____

____

____

break ; default : ____

____

break ; }

2.9.4. do … while p=i=1 ; do {

p=p*i ; On y passe forcément au moins une fois i++ ;

} while (i!=10) ;

(8)

2.9.5. while p=i=1 ;

while (i!=10) {

p*=i ; i++ ; }

2.9.6. for

int i, Somme=0 ;

// n°1 n°2 n°3 for (i=1 ; i<=10 ; i++) // init condit°

// de continuité {

// n°4

Somme+=i ; }

1 2 4 3 2 4 3 2 4 … {

int Somme=0 ;

for (int i=1 ; i<=10 ; Somme+=i, i++) ;

A=0 ; // 9 n°4

2.10. Les fonctions

2.10.1. Déclaration

Toute fonction doit être déclarée avant de pouvoir être utilisée. Cette déclaration a pour but d’indiquer au compilateur le nom de la fonction, le type du résultat retourné, ainsi que le nombre et le type de ses paramètres.

float F1 (int, char, int, float) ; 2.10.2. Définition

La définition d’une fonction est la description de celle-ci comprenant l’entête et le corps de la fonction. L’entête est identique à la déclaration. Le corps est un bloc d’instructions.

float F1 (int A, char B, int C, float D) {

____

____

return A ; }

ex : fonction Max

int Max (int A, int B, int C) {

int M = (A > B) ? A : B ; return (M > C) ? M : C ; }

if (A>B) M=A ; else

M=B ;

(9)

Les définitions de fonctions ne peuvent pas être imbriquées.

2.10.3. Appel d’une fonction

Une fonction ne peut être appelée que si sa déclaration est accessible.

2.10.4. Variables locales et variables globales

Les variables déclarées à l’intérieur d’un bloc sont appelées variables locales.

Leur portée est limitée au bloc.

Les variables globales sont celles qui sont déclarées en dehors d’une fonction ou d’une classe. Elles sont utilisables jusqu’à la fin du fichier.

void F() ;

int A=0 ; // variable globale main ()

{

int B=0 ; // variable locale à main float A ; // variable locale à main F() ;

{

char C ; // C visible jusque

// …

// …

} // là, et c’est tout !

A++ ; // le A du main (pas le premier !) }

void F() {

int C ; ____

}

Variables statiques

int A ; // globale main ()

{

… F() ;

F() ; // récupère le C de l’appel précédent F() ;

… }

void F(void) {

int B=1 ; // locale

static int C=1 ; // statique (reste locale) B++ ;

C++ ; }

(10)

2.10.5. Passage de paramètres

2.10.5.1. Passage par valeur (IN) (recopie)

A l’appel d’une fonction, les paramètres formels sont initialisés avec les valeurs des paramètres réels correspondant.

A la fin de la fonction : aucune recopie de valeur de valeur de formel vers réel.

void Echange (int A, int B) {

int C ; C=A ; A=B ; B=C ; }

int main() {

int X1=1, X2=2 ;

Echange (X1, X2) ; // X1 et X2 inchangés !!!

… }

2.10.5.2. Passage par référence (n’existe pas en C ; plus rapide : pas de recopie) void Echange2 (int &A, int &B)

{

inc C ; C=A ; A=B ; B=C ; }

int main () {

int X1=1, X2=2 ;

Echange2 (X1, X2) ; // X1 et X2 changés

// grâce aux &

}

main () {

TImage Im1 ; Calcul (Im1) ;

… }

void Calcul (const TImage &A) // image non modifiée

{ // grâce à const

… }

Passage par référence constante utilisée pour de gros objets à traiter.

(11)

On a le droit de donner des valeurs par défaut à des paramètres. Elles ne peuvent être mentionnées qu’une seule fois parmi les définition/déclaration d’une fonction.

Par convention, ces valeurs sont généralement mentionnées dans la déclaration de la fonction (le .h).

test.cpp lib.h lib.cpp

#include <lib.h>

main () {

int A=1, B=2, C ; …

C=F (A, B) ; …

}

C=F(A) ;

ça marche puisque valeur par défaut du 2ème paramètre = 0 (c’est défini)

int F (int, int) ;

int F (int, int=0) ;

#include <lib.h>

int F (int X1, int X2) ; {

… }

2.10.5.3. Passage par adresse On verra cela plus tard…

2.10.6. Fonction inline

inline int Min (int A, int B) // pour remplacer l’appel de

{ //la fonction par ce qu’elle fait

return A<B ? A : B ; }

Les définitions de fonctions inline sont dans le .h 2.10.7. Surcharge de fonction

Même nom de fonction différenciées par nombre et/ou type des paramètres (mais pas par le type de retour !!!).

2.10.8. Type du résultat d’une fonction

On retourne ce qu’on veut mais il faut veiller à certaines choses : Ex : ne pas retourner une référence à une variable locale

int F() {

int C ; C=1 ;

return &C ; // problème !!!

}

(12)

2.10.9. Exemple

unit1.cpp unit2.cpp

extern int A ; // décl. var. qui // vient d’ailleurs void F1 () ; // déclaration

static void F2 () ; // déclaration /* F2 ne peut être utilisée que par unit1.cpp */

void F3 () ; // déclaration int main (…)

{ … }

void F1 () // définition {

static int X=1 ; // var. locale // statique int Y=1 ; // var. locale // classique

}

static void F2 () // définition {

… }

int A ; // définition var. globale static int B ; // définition

// utilisable que dans unit2.cpp int C ; // var. globale classique void F3 () ;

static void F4 () ; // déclaration // utilisable que dans unit2.cpp void F3 () // définition

{ … }

static void F4() // définition {

… }

extern int A ; void F1 () ;

static void F2 () ; void F3 () ;

int main (…) {

… }

void F1 () {

static int X=1 ; int Y=1 ;

… }

static void F2 () {

… }

int A ;

static int B ; int C ;

void F3 () ;

static void F4 () ; void F3 ()

{ … }

static void F4() {

… }

(13)

2.11. Les tableaux et les pointeurs

Comme tous les langages, C permet d’utiliser des « tableaux ». On nomme ainsi un ensemble d’éléments de même type désignés par un identificateur unique ; chaque élément est repéré par un « indice » précisant sa position au sein de l’ensemble.

Par ailleurs, comme certains langages tels que le Pascal, le langage C dispose de

« pointeurs », c’est-à-dire de variables destinées à contenir des adresses d’autres « objets » (variables, fonctions…).

A priori, ces deux notions de tableaux et de pointeurs peuvent paraître fort éloignées l’une de l’autre. Toutefois, il se trouve qu’en C un lien indirect existe entre ces deux notions, à savoir qu’un identificateur de tableau est une « constante pointeur ». Cela peut se répercuter dans le traitement des tableaux, notamment lorsque ceux-ci sont transmis en argument de l’appel d’une fonction.

C’est ce qui justifie que ces deux notions soient regroupées dans un seul chapitre.

2.11.1. Les tableaux à un indice

2.11.1.1. Exemple d’utilisation d’un tableau en C

Supposons que nous souhaitions déterminer, à partir de vingt notes d’élèves (fournies en données), combien d’entre elles sont supérieures à la moyenne de la classe. S’il ne s’agissait que de calculer simplement la moyenne de ces notes, il nous suffirait d’en calculer la somme, en les cumulant dans une variable, au fur et à mesure de leur lecture.

Mais ici, il nous faut à nouveau pouvoir consulter les notes pour déterminer combien d’entre elles son supérieures à la moyenne ainsi obtenue. Il est donc nécessaire de pouvoir

« mémoriser » ces vingt notes.

Pour ce faire, il paraît peu raisonnable de prévoir vingt variables scalaires différentes (méthode qui, de toute manière, serait difficilement transposable à un nombre important de notes). Le tableau va nous offrir une solution convenable à ce problème, comme le montre le programme suivant :

#include <stdio.h>

int main(void) {

int i, som, nbm ; double moy ;

int t[20] ;

for (i=0 ; i<20 ; i++) {

printf("donnez la note numéro %d : ", i+1) ; scanf ("%d", &t[i]) ;

}

for (i=0 ; som=0 ; i<20 ; i++) som+=t[i] ;

moy=som/20 ;

printf("\n\n moyenne de la classe : %f\n", moy) ; for (i=0 ; nbm=0 ; i<20 ; I++)

if (t[i]>moy) nbm++ ;

printf("%d élèves ont plus de cette moyenne", nbm) ; return 0 ;

}

(14)

La déclaration : int t[20] réserve l’emplacement pour 20 éléments de type int. Chaque élément est repéré par sa « position » dans le tableau, nommée

« indice ». Conventionnellement, en langage C, la première position porte le numéro 0. Ici, donc, nos indices vont de 0 à 19. Le premier élément du tableau sera désigné par t[0], le troisième par t[2] le dernier par t[19].

Plus généralement, une notation telle que t[i] désigne un élément dont la position dans le tableau est fournie par la valeur i. Elle joue le même rôle qu’une variable scalaire de type int.

2.11.1.2. Quelques règles

a) Les éléments de tableau

Il n’est pas possible, si t1 et t2 sont des tableaux d’entiers, d’écrire t1=t2 ; en fait, le langage C n’offre aucune possibilité d’affectations globales de tableaux, comme c’était le cas, par exemple en Pascal.

b) Les indices

Un indice peut prendre la forme de n’importe quelle expression arithmétique de type entier (ou caractère, compte tenu des règles de conversion systématique).

c) La dimension d’un tableau

La dimension d’un tableau (son nombre d’éléments) ne peut être qu’une constant ou une expression constante.

d) Débordement d’indice

Aucun contrôle de « débordement d’indice » n’est mis en place par la plupart des compilateurs. Pour en comprendre les conséquences, il faut savoir que, lorsque le compilateur rencontre une lvalue telle que t[i], il en détermine l’adresse en ajoutant à l’adresse de début du tableau t, un « décalage » proportionnel à la valeur de i (et aussi proportionnel à la taille de chaque élément du tableau) de sorte qu’il est très facile (s’il on peut dire !) de désigner et, partant, de modifier un emplacement situé avant ou après le tableau.

2.11.2. Les tableaux à plusieurs indices

Comme tous les langages, C autorise les tableaux à plusieurs indices (on dit aussi à plusieurs dimensions).

Par exemple, la déclaration int t[5][3] réserve un tableau de 15 (5*3) éléments. Un élément de ce tableau se trouve alors repéré par deux indices comme dans ces notations : t[3][21] t[i][j] t[i-3][i+j]

Notez bien que, là encore, la notation désignant un élément d’un tel tableau est une lvalue. Il n’en ira toutefois pas de même de notations telles que t[3] ou t[j] bien que, comme nous le verrons plus tard, de telles notations aient un sens en C.

Aucune limitation ne pèse sur le nombre d’indices que peut comporter un tableau. Seules les limitations de taille mémoire liées à un environnement donné risquent de se faire sentir.

2.11.3. Notion de pointeur – les opérateurs * et &

2.11.3.1. Introduction

Nous avons déjà été amené à utiliser l’opérateur & pour désigner l’adresse d’une lvalue. D’une manière générale, le langage C permet de manipuler des

(15)

adresses par l’intermédiaire de variables nommées « pointeurs ». en guise d’introduction à cette nouvelle notion, considérons les instructions :

int *ad ; int n ; n=20 ; ad=&n ;

*ad=30 ;

La première réserve une variable nommée ad comme étant un

« pointeur » sur des entiers. Nous verrons que * est un opérateur qui désigne le contenu de l’adresse qui le suit. Ainsi, à titre « mnémotechnique », on peut dire que cette déclaration signifie que *ad, c'est-à-dire l’objet d’adresse ad, est de type int, ce qui signifie bien que ad est l’adresse d’un entier.

L’instruction ad=&n ;

affecte à la variable ad la valeur de l’expression &n. L’opérateur

& est un opérateur unaire qui fournit comme résultat l’adresse de son opérande. Ainsi, cette instruction place dans la variable ad l’adresse de la variable n.

L’instruction suivante *ad=30 ;

signifie : affecter à la lvalue *ad la valeur 30. Or *ad représente l’entier ayant pour adresse ad (notez bien que nous disons l’entier et pas simplement la valeur car, ne l’oubliez pas, ad est un pointeur sur des entiers).

Bien entendu, ici, nous aurions obtenu le même résultat avec n=30 ;.

2.11.3.2. Quelques exemples

Voici quelques exemples d’utilisation de ces deux opérateurs. Supposez que nous ayons effectué ces déclarations :

int *ad1, *ad2, *ad ; int n=10, p=20 ;

Les variables ad1, ad2 et ad sont donc des pointeurs sur des entiers.

Considérons maintenant ces instructions :

ad1=&n ; ad2=&p ;

*ad1=*ad2+2 ;

Les deux premières placent dans ad1 et ad2 les adresses de n et p. La troisième affecte à *ad1 la valeur de l’expression *ad2+2.

Autrement dit, elle place à l’adresse désignée par ad1 la valeur (entière) d’adresse ad2, augmentée de 2. Cette instruction joue donc ici le même rôle que n=p+2 ;

De manière comparable, l’expression : *ad1+=3 jouerait le même rôle que n=n+3 et l’expression (*ad1)++ jouerait le même rôle que n++.

Remarque : une déclaration telle que int *ad réserve un emplacement pour un pointeur sur un entier. Elle ne réserve pas en plus un emplacement pour un tel entier. Cette remarque prendra encore plus d’acuité lorsque les « objets pointés » seront des chaînes ou des tableaux.

2.11.3.3. Incrémentation de pointeurs

Jusqu’ici, nous nous sommes contenté de manipuler, non pas les variables pointeurs elles mêmes, mais les valeurs pointées. Or si une variable pointeur ad a

(16)

En effet, ad est censée contenir l’adresse d’un entier et, pour C, l’expression ci-dessus représente l’adresse de l’entier suivant. Certes, dans notre exemple, cela n’a guère d’intérêt car nous ne savons pas avec certitude ce qui se trouve à cet endroit.

Mais nous verrons que cela s’avérera fort utile dans le traitement de tableaux ou de chaînes.

Notez bien qu’il ne faut pas confondre un pointeur avec un nombre entier. En effet, l’expression ci-dessus ne représente pas l’adresse de ad augmentée de un (octet). Plus précisément, la différence entre ad+1 et ad est ici de sizeof(int) octets. Si ad avait été déclarée par double *ad ;, cette différence serait de sizeof(double) octets.

De manière comparable, l’expression ad++ incrémente l’adresse contenue dans ad de manière qu’elle désigne l’objet suivant.

2.11.3.4. Comme simuler une transmission par adresse avec un pointeur Nous avons vu que le mode de transmission par valeur semblait interdire à une fonction de modifier la valeur de ses arguments effectifs et nous avions mentionné que les pointeurs fourniraient une solution à ce problème.

Nous sommes maintenant en mesure d’écrire une fonction effectuant la permutation des valeurs de deux variables. Voici un programme qui réalise cette opération avec des valeurs entières :

#include <stdio.h>

void echange(int *ad1, int *ad2) int main(void)

{

int a=10, b=20 ; echange (&a, &b) ; }

void echange(int *ad1, int*ad2) {

int x ; x=*ad1 ;

*ad1=*ad2 ;

*ad2=x ; }

Les arguments effectifs de l’appel de echange sont, cette fois, les adresses des variables n et p (et non plus leurs valeurs). Notez bien que la transmission se fait toujours par valeur, à savoir que l’on transmet à la fonction echange les valeurs des expressions &n et &p.

float B=3.14 ; int A ;

A=1 ;

int *Ptr ; // Ptr pointeur vers un entier

Ptr=&A ; // Ptr reçoit l’adresse de A (Ptr pointe vers A)

*Ptr=2 ; // modifie la zone pointée (pas le pointeur) float *Ptr2 ;

Ptr2=&B ;

*Ptr2=2.4 ;

(17)

2.11.4. Un nom de tableau est un pointeur constant int Tab [10] ;

int *Ptr ; Ptr=Tab ;

Tab correspond à l’adresse du premier entier du tableau.

Ptr aussi.

2.11.4.1. Cas des tableaux à un indice

Ex : rédiger un programme qui définit et remplit un tableau de 10 entiers avec la valeur 1.

int Tab[10] ;

for (int i=0 ; i<10 ; i++) Tab[i]=1 ;

int Tab[10], *Ptr ; Ptr=Tab ;

for (int i=0 ; i<10 ; i++, Ptr++)

*Ptr=1 ; int Tab[10] ;

for (int i=0 ; i<10 ; i++)

*(Tab+i)=1 ; // Tab++ interdit ! 2.11.4.2. Cas des tableaux à plusieurs indices

C’est compliqué donc on passe ! ^^

Rappel : Tab[3][4] crée un tableau de 3 lignes et 4 colonnes.

2.11.5. Les tableaux transmis en argument 2.11.5.1. Cas des tableaux à un indice

a) Tableau de taille fixe int main (…) {

int Tab[10] ; Remplir(Tab) ;

… }

void Remplir (int T[]) // ou void Remplir (int T[10])

{ // ou void Remplir(int *T)

for (int i=0 ; i<10 ; i++) T[i]=5 ; // ou *(T+i)=5 ; }

b) Tableau dont le nombre d’éléments est variable int main (…)

{

int Tab[10] ;

(18)

… }

void Remplir (int *T, int N) {

for (int i=0 ; i<N, i++) T[i]=5 ;

}

2.11.5.2. Cas des tableaux à plusieurs indices int main (…)

{

bool Tab[10][15] ; Toto(Tab) ;

… }

void Toto(bool T[][15]) // Toto (bool **T, int C) {

T[3][4]=true ; // T+1*C+4=true ; }

2.12. Les références

Une référence est un mécanisme d’alias (uniquement C++).

Pointeur, référence : même combat ; sauf que les références ne peuvent pas être vides.

Elles sont toujours initialisées lors de leur déclaration.

int Val=10 ; int *pVal ; pVal=&Val ;

int &RefVal=Val ;

*pVal=12 ; RefVal=13 ;

int main(…) {

int A=1, B=1, C=1 ; F(A, B, &C) ;

//passage ref val adr

void F(int X, int &Y, int *Z) {

X=2 ; Y=2 ;

*Z=2 ; }

Au retour dans main : A=1, B=2, C=2

10 Val pVal

RefVal

12 13

(19)

2.13. Allocation dynamique de mémoire

Allocation d’espace mémoire au moment de l’exécution et non au moment de la compilation.

int main (…) {

int N, *Tab ;

cout << "Nb d’étudiants :" ; cin >> N ;

// alloc. dynamique d’un tableau de N entiers

… }

Version C

int *Tab, *pi ;

pi=(int*)malloc(sizeof(int)) ; Tab=(int*)malloc(N*sizeof(int)) ; if ((pi != NULL) && (Tab != NULL)) {

free (pi) ; free (Tab) ; }

Version C++

int *pi ; int *Tab ; pi = new int ;

Tab = new int [N] ;

if ((pi != NULL) && (Tab != NULL)) {

delete pi ; delete []Tab ; }

(20)

Exemple d’utilisation des pointeurs :

#pragma hdrstop

#pragma argsused

#include <iostream>

#define AffChaine(Ch) cout << Ref++ << "--" << Ch << endl

#define AffCarac(Car) cout << Ref++ << "--" << Car << endl ; // cout << Ref++ affiche Ref puis l’incrémente de 1

using namespace std ;

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

char Ref = 'A' ;

char *Chaine = "merci" ; char Mot[] = "beaucoup" ; char *p = Mot ;

char *Tab[3] = {"ZERO", "UN", "DEUX"} ; AffChaine (Chaine) ;

AffCarac (Chaine[2]) ; AffChaine (Mot+3) ; AffCarac (*++p) ; AffCarac (++*++p) ;

// 3 2 1

AffChaine (Tab[1]) ; AffChaine (Tab[2]+1) ;

Tab[0][2] = '\0' ; // <=> *((*Tab+0)+2) = '\0' ; AffChaine (*Tab) ;

cin.sync() ; cin.get() ; return 0 ; }

Ref 'B' 'C' 'D' 'E' 'F' 'G' 'H' 'I' Chaine

Mot p

Tab

m e r c i \0 b e a u c o u p \0

Z E R O \0 U N \0 D E U X \0 'A'

Affichage : A--merci B--r C--ucoup D--e E--b F--UN G--EUX H--ZE

(21)

2.14. Les chaînes de caractères 2.14.1. Représentation

2.14.1.1. Convention

La chaîne de caractères doit se terminer par le caractère \0 (de code 0).

Chaque caractère est stocké dans un octet.

Si la chaîne peut faire jusqu’à 20 caractères : char Tab[21]

2.14.1.2. Cas des chaînes constantes Ex : char *p ;

p="Toto" ; while (*p)

cout << *p++ ;

=> affichage : T o t o

2.14.1.3. Initialisation de tableaux de caractères char p[20] = "toto" ;

// /!\ p="toto" INTERDIT !!!!

p : t o t o \0 ou

char p[20]={'t', 'o', 't', 'o', '\0'} ; char p[]="toto" ; // <=> p[5]

2.14.1.4. Initialisation des tableaux de pointeurs sur des chaînes char *Tab[7]={"lundi", "mardi", …} ; Tab :

Tab :

=> char Tab[7][9] ;

l u n d i \0 m a r d i \0

l u n d i \0 m a r d i \0 m e r c r e d i \0 j e u d i \0

… … …

(22)

2.14.2. Généralités sur les fonctions portant sur des chaînes char Ch[]="toto" ;

int lg = strlen(Ch) ;

strcat(Ch1, Ch2) ; // concatène et place le résultat ds Ch1 strncat(Ch1, Ch2, lgmax) ; // n’ajoute pas + que lgmax car lg=strcmp(Ch1, Ch2) ; // -> =0 si Ch1 et Ch2 identiques

// <0 si Ch1 < Ch2 // >0 sinon

// /!\ Ch1=Ch2 strcpy(Ch1, Ch2) ; 2.15. Les entrées/sorties

2.15.1. Entrées/sorties en C++

cout << … ; // << et >> définis dans iostream cin >> … ;

int main (…) {

int A=12 ;

float B=123.4567f ; // f pr être vu comme float cout << setw(5) << 5 ; // -> ___12

}

2.15.2. Entrées/sorties en C int A ;

float B ; char C ; char D[10] ;

printf ("bonjour, %d, %f toto%c %s\n", A, B, C, D) ; //affichage 1 2 3 4 1 2 3 4

scanf ("%d%f%c%s", &A, &B, &C, D) ;

//saisie D est déjà une adresse ! 2.16. Les fichiers

2.16.1. Les fichiers en C 2.16.1.1. Introduction

Fichier = quelquechose sur le disque 2.16.1.2. Ouverture / fermeture

FILE *Fich ; //nom logique cstdio (C++) stdio.h (C) Fich=fopen("Nom_Phy", mode) ;

Mode :

"r" en lecture seulement (fichier doit exister)

"w" création et ouverture du fichier en écriture seulement (si le fichier existe, il est détruit)

"a" ouverture d’un fichier existant pour lui ajouter des enregistrements à la suite des enregistrements existants (si le fichier n’existe pas, il est créé)

"r+" ouverture en lecture et écriture d’un fichier qui doit exister

entier réel caractère chaîne de

caractères à la ligne

(23)

"w+" création d’un fichier et ouverture en lecture et écriture (si le fichier existe, il est détruit et remplacé par le fichier créé)

/!\ La fonction fopen retourne NULL si le fichier n’a pas pu être ouvert.

Fermeture : fclose (Fich) ; // Fich nom logique 2.16.1.3. Lecture / écriture

int fgetc(FILE *Flux) ; // Flux nom logique int fputc (int c, FILE *Flux) ;

// c = caractère, donc code ASCII d’où le "int"

char *fgets (char *Ch, int n, FILE *Flux) ; // permet de lire au max n-1 caractères

// arrêt de lire s’il on rencontre le caractère '\n' int fputs (const char *Ch, FILE *Flux) ;

fscanf (FILE *Flux, ______) ;

// idem scanf

fprintf (FILE *Flux, _____) ;

// idem printf

fwrite (&Client, sizeof(enregistrement), n, FICH) ; fread (_______, ______________________, _, ____) ; // adr du taille d’1 élémt nb nom logique // 1er élémt d’élémt du fichier

int A=1 ;

int Tab[10]={1, 2, 3, …, 10} ; FILE *Fich ;

Fich=fopen ("___","w") ;

if (Fich) // si pas NULL -> fichier bien ouvert {

fwrite (Tab, sizeof(int), 7, Fich) ; fwrite (&A, sizeof(int), 1, Fich) ;

// comme fprintf (Fich, "%d", A) ; fclose (Fich) ;

}

fseek (Fich, Dplct, Mode) ; // fonct° de positionnet // type long int

// (nb d’octets)

Mode : 0 depuis le début du fichier 1 depuis la position courante 2 depuis la fin du fichier

rewind(Fich) ;//pr se repositionner au déb du fichier ftell (Fich) ; // retourne un entier qui correspond

(24)

2.16.2. Quelques bases sur les fichiers en C++

Ce dernier paragraphe va nous permettre de voir en détail les possibilités de la librairie iostream. Nous savons déjà qu’il existe deux classes de bases : la classe istream et la classe ostream. Nous connaissons aussi, trois objets prédéfinis : cin, cout et cerr. Mais nous pouvons aller encore plus loin : nous pouvons notamment gérer les accès aux fichiers.

Les entrées-sorties sur les fichiers sont également réalisées avec des flots en C++. Ce type d’opérations nécessite l’inclusion de l’en-tête fstreamen plus de l’en-tête iostream. Les deux grandes classes permettant de réaliser ces opérations sont :

- La classe ofstream : cette classe est dédiée aux écritures réalisées dans des fichiers. La classes ofstream est dérivée de la classe ostream et bénéficie donc de toutes les méthodes définies dans cette classe.

#include <iostream>

#include <fstream>

int main() {

// Deux modes d’ouverture sont possibles :

// - ios::out -> créat°, fichier écrasé si existant // - ios::app -> ajout en fin de fichier

ofstream fichierSortie("donnees.txt", ios::out) ; // Test d’ouverture du fichier

if (!fichierSortie)

cerr << "Pb d’ouverture de fichier" << endl ; exit (1) ;

}

fichierSortie << "J’écris des caractères dans le fichier "

<< "et des nb : " << 10 << " " << 20 << endl ;

// Fermeture

fichierSortie.close() ; }

- La classe ifstream : cette classe est dédiée aux lectures réalisées dans des fichiers. La classe ifstream est dérivée de la classe istream et bénéficie donc de toutes les méthodes définies dans cette classe.

(25)

#include <iostream>

#include <fstream>

int main() {

// Ouverture du fichier

ifstream fichierEntree("donnees.txt", ios::in) ; // Test d’ouverture du fichier

if (!fichierEntree)

cerr << "Pb d’ouverture de fichier" << endl ; exit (1) ;

}

char buf[1024] ;

// Tant qu’il y a des lignes ds le fichier, on les // lit et on les affiche à l’écran

while (!fichierEntree.eof()) {

fichierEntree.getline(buf, 1024) ; cout << buf << endl ;

}

// Fermeture du fichier fichierEntree.close() ; }

Il existe bien entendu d’autres fonctionnalités utilisables sur ces classes : open (idem au contructeur), seek, tell, flush, eof, …

2.17. Espaces de noms 2.17.1. Définition

namespace Projet A { int i ;

int j=10 ; void f() {…} ; void g() ; }

2.17.2. Utilisation int main () {

using namespace Projet A ; j++ ;

}

(26)

2.18. Directives de prétraitement 2.18.1. Forme d’une directive

#_________

2.18.2. #define

#define B 10

// remplace B par 10 dans tout le programme

#define LONG 100

#define LARG (LONG-10) // /!\

main() {

int A = 2*LARG ; // si pas de () : 2*100-10 !!!

#define MAX(a,b) ((a)>(b)?(a):(b)) 2.18.3. #undef

#undef LONG // annule le #define LONG … 2.18.4. #include

#include <…> // C/C++ standard

#include "…" // si c’est perso 2.18.5. Compilation conditionnelle

#if … | #if LANGUE == 1

… | #define ERREUR "Fehlermeldung"

#endif | #elif LANGUE == 2

ou | #define ERREUR "Error"

#ifdef … | #elif …

… | #define …

#endif | #endif

ou

#ifndef …

#endif

File.h File.cpp Test.cpp Pile.cpp

#include "Pile.h"

void G() ;

#include "File.h"

void G() {

… F() ; }

#include "Pile.h"

#include "File.h"

main() {

F() ;

#include "Pile.h"

void F() {

… … } Pile.h

#ifndef PILE_H // si non défini #define PILE_H // on le définit void F() ;

#endif

(27)

2.19. Structure

2.19.1. Déclaration main()

{

struct Client {

char Nom[25] ; int Code ; float ChAff ; } ;

Client UnCl ; // UnCl de type Client 2.19.2. Accès aux membres

UnCl.Code=25 ; UnCl.ChAff=1000 ;

Strcpy (UnCl.Nom, "Dupont") ; 2.19.3. Utilisation

struct Client {

char Nom[25] ; int Age ;

} ;

Client UnClient, *PClient ; UnClient.Age=20 ; PClient=&UnClient ;

(*PClient).Age=30 ; //PClient -> Age = 30 ; struct Elt

{

Client Donnees ; Elt *pSuivant ; }

nouveau type

Références

Documents relatifs

– String readUTF() lecture d'un chaîne de caractère au format UTF-8 – Object readObject() lecture d'un objet sérialisable.. Exemple2 :

(Collagenous OR lymphocytic) colitis 1814 réf.. Qui peut le moins peut

Document 9 – Passage onirique en quatre couloirs,

Si elle est utilisée depuis toujours pour parfumer le linge et chasser les mites, l’huile essentielle de lavande se retrouve dans de nombreux parfums et eaux de toilette, mais

[r]

L’huile désodorisée coule du dernier compartiment du désodoriseur vers un filtre pour empêcher les impuretés qui sont formés sous l’effet de haute température,

Formation VHDL Vahid MEGHDADI.. Conception

Chacun des critères doit être évalué avec une note de 1 à 5, par l’ensemble du groupe