0% ont trouvé ce document utile (0 vote)
44 vues14 pages

Introduction aux tableaux et récursivité en C++

Le document explique les concepts de tableaux, de récursivité, de gestion de fichiers et de pointeurs en programmation. Il décrit comment déclarer et initialiser des tableaux, utiliser des fonctions récursives, gérer les entrées/sorties avec des fichiers, et manipuler des pointeurs et des références. Des exemples de code illustrent chaque concept pour faciliter la compréhension.

Transféré par

ousmanetr67
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)
44 vues14 pages

Introduction aux tableaux et récursivité en C++

Le document explique les concepts de tableaux, de récursivité, de gestion de fichiers et de pointeurs en programmation. Il décrit comment déclarer et initialiser des tableaux, utiliser des fonctions récursives, gérer les entrées/sorties avec des fichiers, et manipuler des pointeurs et des références. Des exemples de code illustrent chaque concept pour faciliter la compréhension.

Transféré par

ousmanetr67
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

2.4.

Les tableaux
2.4.a Qu'est-ce qu'un tableau ?
Un tableau est un ensemble de variables de même type. Chacune d'elles est appelée élément
du tableau. Tous les éléments sont référencés à l'aide u nom du tableau et son stockés dans des
emplacements contigus en mémoire.

2.4.b Déclarer et indexer un tableau statique


Syntaxe générale : type Nom[taille1] [taille2]….. [tailleN]

L'indicateur type correspond à l'indicateur du type de données stockées dans le tableau.


L'élément Nom est le nom du tableau déclaré. N est le nombre de dimension du tableau, et
taille1, taille2, taille N définissent le nombre d'éléments du tableau dans chaque dimension, et
doivent figurer entre crochets. Il est fortement conseiller de déclarer la taille comme un entier
constant ( int const ) et de l’initialiser, puis de l’utiliser dans la déclaration du tableau, comme
dans l’exemple suivant :

Exemple :

Int const n=8

int tableau_entier[n];

Est un tableau de type entier, de nom tableau_entier qui contient 8 éléments

Les tableaux sont indexés de 0 à n-1 (n étant la taille du tableau). Le premier élément
correspond à l'indice 0, le deuxième à l'indice 1,…, et le denier élément correspond à l'indice
n-1 et non n.

2.4.c Initialisation d’un tableau


Cette opération peut se faire de différentes manières selon le contexte du programme,
1ère méthode : en donnant les valeurs élément par élément dans le programme.
2ème méthode : en donnant les valeurs de tous les éléments d’un coup, entre accolades
3ème méthode : si les valeurs initiales des éléments du tableau sont les mêmes, ou
réponde à une expression particulière, ou même peuvent être saisie au clavier, il est
possible d’utiliser une boucle for, avec comme compteur l’indice des éléments.
Dans l’annexe A, vous trouverez le programme récapitulant ces trois méthodes pour
un tableau d’une seule dimension de 5 éléments.

2.5 La récursivité
Qu'est-ce que la récursivité ? D'un point vu théorique la récursivité est la capacité d'un
programme ou d'une fonction à s'appeler, ce qui peut être désigné par l'auto-appel. Elle
permet de replacer les boucles (for, while,…..)
La récursivité relève cependant plus de l'algorithmique que du langage lui-même.
La récursivité en programmation ne doit pas être confondu avec la récursivité
mathématique

2.5.a Les fonctions récursives :


Une fonction récursive est une fonction qui s’auto-appel. L’exemple le plus utilisé pour
illustrer ce type de fonction, et la fonction factorielle notée (mathématiquement) « ! » , définit
comme suit :

𝑛! = 1 × 2 × … × 𝑛

0! = 1

Où n est un entier dont on veut calculer le factoriel.

Le code relatif à cette fonction est donné dans

Figure 2-6 FactorielProject

2.6 Les fichiers


Jusqu’à présent nous n’avons gérer les entrées et sorties que vers et de la console de
commande (cmd), ceci peut se faire grâce au fichier en-tête iostream (input/output stream)
autrement dit (flux d’entrées/sorties). Ce fichier est inclus par défaut dans tous les fichiers lors
de leur première ouverture, comme dans CodeBlocks par exemple on retrouve
systématiquement #include <iostream> .

Cependant, il est souvent nécessaire d’interagir avec des fichiers, pour lire ou stocker des
données par exemple, dans ces cas on utilise des flux vers des fichiers, cela en incluant le
fichier en-tête fstream (file stream) qui gère les entrées et sorties vers et à partir d’un fichier.
Input/output stream Console
iostream
Flux d’entrées/sortie

File stream
fstream Fichier
Flux vers un

Figure 2-7 gestion des flux en C++

Dans ce qui suit nous introduirons les principales commandes pour la gestion des fichiers en
C++. La principale différence avec un flux vers une console, c’est que pour les fichiers on
devra spécifier s’ils sont ouverts en Ecriture ou en Lecture dès le départ.

2.6.a ouverture d’un fichier en écriture :


On devra ouvrir un flux pour chaque fichier à utiliser. On considèrera dans un premier temps
le flux comme une variable (une variable un peu particulière, on le verra plus loin).
Caractérisée par (en cadrée en rouge dans le code figure 2-8):

 Son type : ofstream (output file stream), qui est définit dans une bibliothèque
“fsteam“ qui signifie, file stream, elle gère les flux vers et des fichiers.
 Sa valeur : c’est le chemin d’accès au fichier en question, si le fichier existe il sera
ouvert en écriture directement, s’il n’existe pas, il sera créé. Si le chemin d’accès est
faut, rien ne se passe, il n’y aura pas de message d’erreur, il est donc recommander
d’ajouter un test qui nous permet de vérifier l’existence du fichier .
Figure 2-8 Code pour ouverture d'un fichier en écriture

Dans ce cas mon fichier n’existe pas. Afin d’illustrer la création du fichier, il a été
rajouté dans le code de la figure 2-8 les instructions relatives à la date et l’heure
(surlignés en jaune sur la figure).
La figure suivante donne le résultat de l’exécution :

Figure 2-9 création et modification d'un fichier

Sur la figure 2-9, on visualise trois fenêtres :

 En haut à droite : la console où s’est exécuté le code ;


 A gauche : le répertoire où a été crée le fichier FichierTest, c’est un fichier texte, la
fenêtre blanche indique les propriétés du fichier.
Surligné en magenta la date et l’heure de l’exécution, qui correspond à celle de la
création du fichier.

Cependant il est contraignant d’aller à chaque fois au répertoire de travail, pour


vérifier si le fichier a été créé ou non ! il est donc plus judicieux d’introduire dans le code un
test, qui permet de le vérifier, comme indiquer dans le code suivant de la figure 2-10.

Figure 2-10 Problème Lors de la création ou l'ouverture d'un fichier, ici le chemin d’accès est faux

On a introduit dans le code de la figure 2-10, une erreur volontaire, il s’agit ici d’une erreur du
chemin d’accès, qui devrait être le même que celui de la figure 2-9
(D:/CodeBlocs/GestionFichiers/[Link]).

Remarques :

 Il est important de noter que si l’erreur s’introduit dans le nom du fichier, à


l’exécution un autre fichier sera créé. Si par exemple, vous voulez utiliser un fichier
nommé [Link] existe, et vous réexécuter le code avec un fichier
[Link], où le “R“ a remplacer par erreur le “T“, un autre fichier de ce dernier
nom sera créé.

2.6.b écrire dans un fichier :


L’écriture dans un fichier se passe comme pour cout, en utilisant les chevrons <<. Lors de
l’ouverture d’un fichier existant l’écriture dans ce dernier se fera dès le début, c’est-à-dire que
toutes les données déjà inscrites vont être écrasées par les nouvelles données.

La figure 2-11 représente un code qui permet d’écrire dans un fichier ; Il est facile de tester ce
qui a été dit précédemment, concernant le contenu du fichier. Il suffit d’écrire dans le fichier
une première fois (compiler et exécuter une première fois), ouvrir votre fichier pour vérifier le
contenu, le refermer. Puis modifier le texte à écrire dans le fichier, recompiler et réexécuter.
Lors de la réouverture de votre fichier, vous verrez que le contenu a changé
Figure 2-11 Ecriture dans un fichier

Dans le code, surlignées en jaune, les deux lignes de texte qu’on retrouve dans le fichier
[Link], entouré en rouge, la ligne qui replacera le contenu tu texte lors de la seconde
compilation et exécution, ici elle est grisée car en commentaire.

Remarque : bien que normalement il n’est pas nécessaire de le préciser, on le note au cas où
cela échappera au plus novices : Avant la seconde compilation les lignes surlignées en jaune
seront mises en commentaire, et la ligne entourée en rouge “décommenté“.

La question qu’on doit se poser donc est : comment peut-on écrire dans un fichier sans écraser
ce qui y est déjà ?

Pour ce faire, on doit compléter notre instruction d’écriture comme suit :

ofstream MonFlux("D:/CodeBlocs/GestionFichiers/[Link]",ios::app);

Où a été ajouté l’instruction ios ::app, il n’est pas judicieux d’explique la syntaxe de cette
instruction pour le moment, ce n’est pas le propos de ce chapitre, il sera abordé plus tard.

2.6.c Lecture d’un fichier


Avant toute chose, il faut, comme pour l’écriture, ouvrir un flux en lecture, cela en utilisant
l’instruction ifstream (qui fait aussi partie de la bibliothèque fstream)

La lecture dans un fichier répond au même principe que l’écriture, à la différence qu’il y a
trois modes de lecture :

 Caractère par caractère,


 Mot par mot,
 Ligne par ligne.

Figure 2-12 Lecture d’un fichier

Il est à noter que dans le code, il est nécessaire de créer une variable qui contiendra la valeur
lue, ainsi :

 Pour la lecture d’une ligne avec getline(), on déclare une chaine de caractères (string) ;
 Pour la lecture d’un mot en utilisant les chevrons >> on déclare une chaine de
caractères (string), un réel (double) ou un entier (int) selon ce qui doit être lu ;
 Pour la lecture d’un caractère en utilisant , on déclare un caractère (char) ;

2.7 Les pointeurs


Les pointeurs sont des variables particulières. En réalité derrière chaque déclaration de
variable il y a un pointeur (non déclaré !).

2.7.a Operateur d’adresse :


Pour mieux comprendre, on va prendre l’exemple d’un entier n, pour le déclarer on utilise
l’instruction int n ; qui aura pour effet de réserver un espace mémoire dans la pile de la
machine sur laquelle on travail.
Figure 2-13 Mémoire réservée pour une variable

Cet espace ou case mémoire devra être identifier. On utilise pour cela un adresse mémoire,
cette adresse correspond à un nombre attribué à chaque case. On pourrait imaginer que les
cases mémoires sont des maisons tout le long d’une rue, qui sont identifiées par les numéros
de portes. L’adresse d’une case mémoire correspondra à l’adresse postale de chaque maison.

Ainsi à chaque fois qu’on déclare une variable, et qu’on lui attribue de ce fait, un espace
mémoire, ce dernier sera identifié par une adresse ; Cette adresse peut être lu grâce à
l’opérateur d’adresse &

Figure 2-14 Opérateur d’adresse

Dans la figure 2-14, le code demande d’afficher l’adresse de n, grâce à l’instruction &n, et
lors de l’exécution on obtient 0x6dfeec, c’est l’adresse de n, elle est donnée en hexadécimal
(base 16, qui prend les caractères 0-6 et a-f).

Remarque : l’adresse de n est 6dfeec, le 0x est un préfixe où

 Le 0 permet au compilateur de comprendre que c’est un nombre,


 Le x lui permet d’identifier qu’il est en hexadécimal ( o pour l’octal, et b en binaire).
2.7.b déclaration de pointeur
Pour déclarer un pointeur, il faut comme pour une variable, donner :

 Son nom,
 Et son type, cependant ici, le type renvoi vers le type de variable que ce pointeur
adresse.
 Il faudra rajouter un * pour identifier que c’est un pointeur ( sinon sa déclaration sera
la même que celle d’une variable !).

Exemple :

int main()

int n,*p;

cout<<"l'adresse de n est :"<<&n<<endl;

p=&n;

cout<<"p="<<p<<endl;

return 0;

Dans le code sont déclarés un entier n, et un pointeur vers un entier p (surligné en vert).

p est une variable, dans laquelle on pourra stocker des adresses, comme ici on lui donner la
valeur de l’adresse de n.

Il est important de remarquer, que toute tentative d’y stocker autre chose qu’une adresse,
générera une erreur.

2.7.c un peu d’arithmétique


Sans trop renter dans les détails, on dira que les pointeurs sont régit par des règles
d’arithmétique qui sont conditionnés par les types de variables adressées par ces pointeur.

Ici, il est utile de se rappeler l’instruction sizeof, qui nous indique le nombre d’octets réservé à
chaque variable, ou type de variable. Le code et son exécution donnés figure 2-15 est un bref
aperçu de l’arithmétique des pointeurs
Figure 215: arithmétique des pointeurs

Ainsi, il est possible d’additionner ou de soustraire un entier n d’un pointeur, ce qui aura
comme effet de déplacer l’adresse (valeur du pointeur) d’un nombre n*sizeof(type du
pointeur) d’octets.

Par exemple, dans le code de la figure 2-15, incrémente donc ajoute un entier n=1 au pointeur
p qui au départ à une valeur de 6dfee4 ( à rappeler que 0x est là pour indiquer qu’il s’agit d’un
nombre en hexadécimal), ce qui donne :

6dfee4 +4 = 6dfee8

2.7.d operateur de déférencement *


Si on déclare un pointeur p vers un type de variable, alors *p sera du type de la variable et
aura comme valeur le contenu de l’adresse valeur de p.

La figure 2-16 indique comme peut être utilisé l’opérateur de déférencement * ;

Dans se code

 Ligne 7 : sont déclarer un entier i initialisé à 2, et un pointeur vers un entier.


 Ligne 8 : le pointeur aura comme valeur l’adresse de i.
 Lignes 9-12 : affichage des différentes valeurs.
 Ligne 13 : la valeur 5 est affectée au contenu de la case mémoire pointé par p.
 Ligne 14 : affichage de la nouvelle valeur de i.
Figure 2-16 Operateur de Déférencement

2.8 Pointeurs et références


Une référence est une étiquette qu’on donne à une variable déjà existante, c’est comme
donner un autre nom à la variable en question. On peut donc donner autant d’étiquettes que
nécessaire pour une seule variable.

Une étiquette doit impérativement être initialisé lors de sa déclaration, on lui affectera
forcement la variable à laquelle elle renvoi.

La figure 2-17 donne une image du code donné en figure 2-18


étiquettes
ri1 ri2

Adresse i :6fdee0 i

Figure 2-17 Adressage et étiquetage d'une case mémoire

En considérant la variable i, dont l’adresse est stockée dans le pointeur p (=6fdee0 dans notre
cas), on lui attribue deux étiquètes ri1 et ri2.

Figure 2-18 code pour illustrer la déclaration d'étiquètes


On voit bien sur le code de la figure 2-18 comme doivent être déclarer et initialiser les
étiquètes (ligne 9 du code). Lors de l’exécution, on voit bien que ri1 et ri2 ont ma même
valeur que i (=2), mais aussi la même adresse 6dfee0.

Quelle est donc l’intérêt d’utiliser des références (étiquètes) puisqu’elles ont même valeur et
même adresse que leur variable cible ?

L’utilité des références apparait lors du passage de valeur pas adresse, sachant que pour
pouvoir lire le contenu d’une case mémoire dont l’adresse contenu dans le pointeur p, on
utilise l’opérateur de déférencement * et donc l’instruction *p (voir le code figure 2-16).

Figure 2-19 Comparaison de l'utilisation de pointeur et de reference


(Ref:[Link]

Pour bien comprendre, la figure 2-19 donne une comparaison de l’utilisation des pointeurs et
des références. Une même fonction est écrite en utilisant les pointeurs dans un premier temps
puis les références. On voit clairement sur cet exemple que la syntaxe de la fonction
(minmax_pointeur) avec pointeur ainsi que son appel peuvent générer des erreurs à cause de
l’utilisation récurrente des opérateurs * et & ; alors que la seconde version, avec les références
(minimax_reference) est plus légère sur le plan syntaxique, ce qui générera moins de risque
d’erreurs

2.9 Pointeurs et tableaux


Les tableaux sont étroitement liés aux pointeurs. Tout tableau est un pointeur constant sur lui-
même.

En effet, lorsqu’on déclare un tableau, la machine réserve des espaces mémoire contigus
(voisins) les uns aux autres pour tous les éléments du tableau, et seule l’adresse du premier
élément est nécessaire, car celles des autres éléments s’en déduisent.
Le code de la figure 2.20 l’illustre assez bien.

Figure 2-20 Les Tableaux et les Pointeurs


Dans ce code (figure 2.20) on a :

Ligne 7 : déclaré un tableau, nommé Tab, de cinq éléments entiers.

Ligne 8 : déclaré un entier i, et un pointeur vers un entier p ;

Ligne 9 : on affecte à p, la valeur Tab. Ici il est important de remarquer que si Tab n’était pas
un tableau mais juste un entier scalaire, cette affectation générerait une erreur de type.

Ligne 11 : on affiche l’adresse du tableau, ici aussi, il est important de remarquer qu’on a pas
eu besoin d’utiliser l’opérateur d’adresse, cette instruction avec un entier scalaire aurai
retourné la valeur de la variable et non son adresse.

Vous aimerez peut-être aussi