Nous utilisons des cookies pour améliorer votre expérience. En poursuivant votre navigation sur ce site, vous acceptez l'utilisation de cookies.


Politique de confidentialité
Introduction et syntaxe de base
Pointeurs et fonctions
Programmation OO
Structures de données
La bibliothèque standard (STL)

Les tableaux en C++ - Déclaration et interêts

 

Les tableaux en C++ - Déclaration et interêts

Les variables que vous avez créées jusqu'à présent ne peuvent stocker qu'un seul élément de données du type spécifié : un entier, une valeur à virgule flottante, un caractère ou une valeur booléenne. Un tableau stocke plusieurs éléments de données du même type. Vous pouvez créer un tableau d'entiers ou un tableau de caractères (ou en fait un tableau de n'importe quel type de données), et il peut y en avoir autant que la mémoire disponible le permet

Un tableau est une variable qui représente une séquence d'emplacements mémoire, chacun stockant un élément de données du même type. Supposons, par exemple, que vous ayez écrit un programme pour calculer la note moyenne d'une classe. Vous voulez maintenant étendre le programme pour calculer combien d'échantillons sont au-dessus de cette moyenne et combien sont en dessous. Pour ce faire, vous devez conserver les données d'échantillon d'origine, mais le stockage de chaque élément de données dans une variable distincte serait difficile à coder et très peu pratique. Un tableau vous donne les moyens de le faire facilement. Vous pouvez stocker 20 échantillons de notes dans un tableau défini comme suit :

Exemple 1
double notes[20];       // Définir un tableau de 20 notes

Cet exemple définit un tableau portant le nom notes pour stocker 20 valeurs de type double. Les valeurs de données sont appelées éléments. Le nombre d'éléments spécifiés entre les parenthèses est la taille du tableau. Les éléments du tableau ne sont pas initialisés dans cette instruction, ils contiennent donc des valeurs inutiles.

La taille d'un tableau doit toujours être spécifiée en utilisant une expression entière constante. Toute expression entière que le compilateur peut évaluer au moment de la compilation peut être utilisée, bien que la plupart du temps il s'agisse d'un littéral entier ou d'une variable entière constante qui a elle-même été initialisée à l'aide d'un littéral.

Vous vous référez à un élément de tableau en utilisant un nombre entier appelé indice. L'indice d'un élément particulier du tableau est son décalage par rapport au premier élément. Le premier élément a un décalage de 0 et donc un indice de 0 ; un indice de 3 fait référence au quatrième élément du tableau, soit trois éléments à partir du premier. Pour faire référence à un élément, vous placez son indice entre crochets après le nom du tableau. Ainsi, pour définir le quatrième élément du tableau notes à 17,5, vous devez écrire ce qui suit :

Exemple 2
notes[3] = 17.5;

Vous pourriez définir le tableau avec des valeurs initiales appropriées comme ceci :

Exemple 3
int notes[6] {10.5, 9.0, 17.5, 18.75, 4.5, 8.75};

Vous utilisez des références à des éléments individuels du tableau comme des variables entières ordinaires dans une expression. Comme vous l'avez vu précédemment, un élément de tableau peut se trouver à gauche d'une affectation pour définir une nouvelle valeur. Vous pouvez donc copier la valeur d'un élément vers un autre dans une affectation, comme ceci :

Exemple 4
notes[3] = notes[2];

Cependant, vous ne pouvez pas copier toutes les valeurs des éléments d'un tableau vers les éléments d'un autre tableau dans une affectation. Vous ne pouvez opérer que sur des éléments individuels. Pour copier les valeurs d'un tableau dans un autre, vous devez copier les valeurs une par une. Vous avez besoin d'une boucle.

Plus loin dans ce tutoriel, nous parlerons de STL où vous pouvez utiliser certaines fonctions prédéfinies pour manipuler efficacement les tableaux.

Vous pouvez omettre la taille du tableau lorsque vous fournissez une ou plusieurs valeurs initiales dans sa définition. Le nombre d'éléments sera le nombre de valeurs initiales. Voici un exemple :

Exemple 5
int valeurs[] {2, 3, 4};

Cela définit un tableau avec trois éléments de type int qui auront les valeurs initiales 2, 3 et 4. C'est équivalent à écrire ceci :

Exemple 6
int valeurs[3] {2, 3, 4};

L'avantage d'omettre la taille est que vous ne pouvez pas vous tromper dans la taille du tableau ; le compilateur la détermine pour vous.

Tableaux multidimensionnels

Les tableaux peuvent être multidimensionnels en ajoutant plusieurs séries de crochets. Comme pour les tableaux unidimensionnels, ils peuvent être remplis un par un ou tous en même temps pendant la déclaration.

Exemple 7
int Tab[2][2] = { { 0, 1 }, { 2, 3 } };
Tab[0][0] = 0;
Tab[0][1] = 1;
        

Les accolades supplémentaires sont facultatives, mais leur inclusion est une bonne pratique car elle facilite la compréhension du code.

Exemple 8
int Tab[2][2] = { 0, 1, 2, 3 }; // alternative

Tableaux dynamiques

Comme les tableaux précédents sont constitués de mémoire statique (non dynamique), leur taille doit être déterminée avant l'exécution. Par conséquent, la taille doit être une valeur constante. Pour créer un tableau dont la taille n'est pas connue avant l'exécution, vous devez utiliser de la mémoire dynamique, qui est allouée avec le mot-clé new et doit être affectée à un pointeur.

Exemple 9
int* Tab = new int[3]; // tableau alloué dynamiquement

Un tableau en C++ se comporte comme un pointeur constant vers le premier élément du tableau. Le référencement des éléments d'un tableau peut donc se faire tout aussi bien avec l'arithmétique des pointeurs. En incrémentant le pointeur de un, vous vous déplacez vers l'élément suivant du tableau, car les modifications de l'adresse d'un pointeur sont implicitement multipliées par la taille du type de données du pointeur.

Exemple 10
*(tab+1) = 10; // Tab[1] = 10;

Taille du tableau 

Comme avec n'importe quel autre pointeur, il est possible de dépasser la plage valide d'un tableau et donc de réécrire une partie de la mémoire adjacente. Cela doit toujours être évité car cela peut conduire à des résultats inattendus ou faire planter le programme.

Exemple 11
int Tab[2] = { 1, 2 };
Tab[2] = 3; // erreur: out of bounds (hors limites)
        

Pour déterminer la taille d'un tableau ordinaire (alloué statiquement), vous pouvez utiliser la fonction std::size

Exemple 12
#include <iostream>

using namespace std;

int main(void){

  int Tab[3] = { 1, 2, 6 };
  int taille = std::size(Tab);
  cout << "Nombre d'éléments : " << taille << '\n';
  
  return 0;
}
        
Nombre d'éléments : 3

Cette méthode ne peut pas être utilisée pour les tableaux alloués dynamiquement. Le seul moyen de déterminer la taille d'un tel tableau est la variable utilisée pour son allocation.

Exemple 13
int taille = 3;
int* Tab = new int[taille]; // tableau alloué dynamiquement
        

Lorsque vous avez fini d'utiliser un tableau dynamique, vous devez penser à le supprimer. Pour ce faire, utilisez le mot-clé delete accompagné d'un ensemble de crochets.

Exemple 14
delete[] Tab; // libérer le tableau alloué (Même syntaxe pour le tableau multidimensionnel)
Tab = nullptr; // marquer le pointeur comme inutilisé
        

Pointeurs et tableaux 

Il existe un lien étroit entre les pointeurs et les noms de tableaux. En effet, il existe de nombreuses situations dans lesquelles vous pouvez utiliser un nom de tableau comme s'il s'agissait d'un pointeur. Un nom de tableau en lui-même se comporte généralement comme un pointeur lorsqu'il est utilisé dans une instruction de sortie, par exemple. Autrement dit, si vous essayez d'afficher un tableau en utilisant simplement son nom, vous n'obtiendrez que l'adresse hexadécimale du tableau, à moins qu'il ne s'agisse d'un tableau de caractères, bien sûr, pour lequel tous les flux de sortie standard supposent qu'il s'agit d'une chaîne de caractères (style-C). Comme un nom de tableau peut être interprété comme une adresse, vous pouvez également en utiliser un pour initialiser un pointeur :

Cela stockera l'adresse des valeurs du tableau dans le pointeur ptr. Bien qu'un nom de tableau représente une adresse, ce n'est pas un pointeur. Vous pouvez modifier l'adresse stockée dans un pointeur, tandis que l'adresse représentée par un nom de tableau est fixe.

Exemple 15
double valeurs[10];
double* ptr {valeurs};
        

Arithmétique du pointeur

Vous pouvez effectuer des opérations arithmétiques sur un pointeur pour modifier l'adresse qu'il contient. Vous êtes limité à l'addition et à la soustraction pour modifier l'adresse contenue dans un pointeur, mais vous pouvez également comparer des pointeurs pour produire un résultat logique. Vous pouvez ajouter un entier (ou une expression qui évalue à un entier) à un pointeur, et le résultat est une adresse. Vous pouvez soustraire un entier d'un pointeur, ce qui donne également une adresse. Vous pouvez soustraire un pointeur d'un autre, et le résultat est un entier, pas une adresse. Aucune autre opération arithmétique sur les pointeurs n'est légale.

L'arithmétique avec des pointeurs fonctionne d'une manière particulière. Supposons que vous ajoutiez 1 à un pointeur avec une instruction telle que celle-ci :

++ptr;

Cela incrémente apparemment le pointeur de 1. La manière exacte dont vous incrémentez le pointeur de 1 n'a pas d'importance. Vous pourriez utiliser une affectation ou l'opérateur += pour obtenir le même effet, de sorte que le résultat serait exactement le même avec cette instruction :

ptr += 1;

L'adresse stockée dans le pointeur ne sera cependant pas incrémentée de 1 au sens arithmétique normal. L'arithmétique des pointeurs suppose implicitement que le pointeur pointe vers un tableau. Incrémenter un pointeur de 1 signifie l'incrémenter d'un élément du type vers lequel il pointe. Le compilateur connaît le nombre d'octets nécessaires pour stocker l'élément de données vers lequel pointe le pointeur. L'ajout de 1 au pointeur incrémente l'adresse de ce nombre d'octets. En d'autres termes, l'ajout de 1 à un pointeur incrémente le pointeur de sorte qu'il pointe sur l'élément suivant du tableau. Par exemple, si ptr est "pointeur vers double" et que le type double est de 8 octets, alors l'adresse dans ptr sera incrémentée de 8.

En général, l'expression ptr + n, dans laquelle n peut être n'importe quelle expression résultant en un entier, ajoutera n * sizeof(double) à l'adresse dans ptr parce que ptr est de type "pointeur vers double".

La même logique s'applique à la soustraction d'un entier à un pointeur. Si ptr contient l'adresse de valeurs[2], l'expression ptr - 2 est évaluée à l'adresse du premier élément du tableau, valeurs[0]. En d'autres termes, l'incrémentation ou la décrémentation d'un pointeur fonctionne en fonction du type de l'objet pointé. L'incrémentation d'un pointeur sur long par 1 change son contenu en l'adresse longue suivante et incrémente donc l'adresse de sizeof(long) octets. Le fait de le décrémenter de 1 décrémente l'adresse de sizeof(long).

Rappelez-vous qu'une expression telle que ptr + 1 ne change pas l'adresse dans ptr. Il s'agit simplement d'une expression qui donne un résultat du même type que ptr. En revanche, les expressions ++ ptr et ptr += n modifient le ptr.

Utilisation de la notation des pointeurs avec un nom de tableau : 

Vous pouvez utiliser un nom de tableau comme s'il s'agissait d'un pointeur permettant d'adresser les éléments du tableau. Supposons que vous définissiez ce tableau :

Exemple 18
long notes[5] {};

Vous pouvez vous référer à l'élément notes[3] en utilisant la notation par pointeur comme *(notes + 3). Cette notation peut être appliquée de manière générale de sorte que pour les éléments notes [0], notes [1], notes [2], ..., vous pouvez écrire *notes, *( notes + 1), *( notes + 2), et ainsi de suite. Le nom du tableau en lui-même fait référence à l'adresse du début du tableau, donc une expression telle que notes +2 produit l'adresse de l'élément situé à deux éléments du premier.

Vous pouvez utiliser la notation de pointeur avec un nom de tableau de la même manière que vous utilisez un indice entre crochets - dans des expressions ou à gauche d'une affectation. Vous pouvez lire les valeurs du tableau de notes de l'utilisateur en utilisant la syntaxe suivante :

Exemple 19
#include <iostream>

using namespace std;

int main(void){

  double notes[3];
  for (int i=0; i < std::size(notes); ++i)
  {
        cout << "Saisir une note : ";
        cin >> *(notes + i);
  }

  for (int i=0; i < std::size(notes); ++i)
  {
        cout << *(notes + i) << '\t';
  }
  
  return 0;
}
        

L'expression *(notes + i) fait référence aux éléments successifs du tableau.

Partager ce cours avec tes amis :
Rédigé par ESSADDOUKI Mostafa
ESSADDOUKI
The education of the 21st century opens up opportunities to not merely teach, but to coach, mentor, nurture and inspire.