Aller au contenu

Programmation C++/Les tableaux

Un livre de Wikilivres.

Les tableaux à une dimension

Les tableaux sont des structures de données constituées d'un certain nombre d'éléments de même type. On peut accéder directement à un élément du tableau en indiquant son indice entre crochets (indice de 0 à nombre_d_éléments-1).

Les tableaux statiques

Syntaxe

type identificateur[taille];

Sémantique

identificateur est un tableau de taille éléments de type type. taille est obligatoirement une valeur constante. La taille du tableau est donc figée une fois pour toute et ne peut pas être modifiée en cours d'exécution.

Pour désigner la i-ième case de tableau, on écrit

identificateur[i]

i est un entier quelconque. Les cases sont numérotées à partir de 0 : les valeurs possibles de i vont donc de 0 à taille-1.

Pointeurs et tableaux

Il existe un lien entre les pointeurs et les tableaux : identificateur est en fait un pointeur constant vers un élément de type type, pointant le premier élément du tableau, en d'autres termes le nom d'un tableau est un pointeur constant sur le premier élément du tableau.

Exemple

#include <iostream>


using namespace std;

int main()
{
  int i;

  int const tailleTableau(10); //taille du tableau
  int tableau[tailleTableau]; //déclaration du tableau

  for (int i(0); i<tailleTableau; i++ )
  {
      tableau[i]=i*i;
      cout<<"Le tableau ["<<i<<"] contient la valeur "<<tableau[i]<<endl;
  }

   return 0;
}
Exécution

Le tableau [0] contient la valeur 0

Le tableau [1] contient la valeur 1

Le tableau [2] contient la valeur 4

Le tableau [3] contient la valeur 9

Le tableau [4] contient la valeur 16

Le tableau [5] contient la valeur 25

Le tableau [6] contient la valeur 36

Le tableau [7] contient la valeur 49

Le tableau [8] contient la valeur 64

Le tableau [9] contient la valeur 81

Les tableaux dynamiques

Un tableau dynamique est un tableau dont le nombre de cases peut varier au cours de l'exécution du programme. Il permet d'ajuster la taille du tableau au besoin du programmeur.

L'opérateur new

Syntaxe :

pointeur=new type[taille];

L'opérateur new permet d'allouer une zone mémoire pouvant stocker taille éléments de type type, et retourne l'adresse de cette zone. Le paramètre taille est un entier qui peut être quelconque (variable, constante, expression). new renverra un pointeur vers un type. La variable pointeur est donc du type type *. Les cases du tableau seront numérotées de 0 à taille-1 et on y accédera comme un tableau statique. S'il n'y a pas assez de mémoire disponible, new renvoie le pointeur NULL.

L'opérateur delete[]

Syntaxe :

delete[] pointeur;

Cette utilisation de delete[] permet de détruire un tableau précédemment alloué grâce à new. N'oubliez surtout pas les crochets juste après delete, sinon le tableau ne sera pas correctement libéré. Le programmeur en C++ doit gérer la destruction effective des tableaux qu'il a créé dynamiquement.

Exemple

#include <iostream>
using namespace std;

int main()
{
    int i, taille;

    cout << "Tapez la valeur de taille : ";
    cin >> taille;
    int *t;
    t = new int[taille];

    for (i = 0; i < taille; i++)
        t[i] = i * i;

    for (i = 0; i < taille; i++)
       cout << t[i] << endl;
    delete[] t;

    return 0;
}
Exécution 1
Tapez la valeur de taille : 4
0 
1
4
9
Exécution 2
Tapez la valeur de taille : 6
0
1
4
9
16
25

Tableaux multidimensionnels

Un tableau peut avoir plus d'une dimension (matrice 2D, matrice 3D, ..). Par contre, on ne peut pas utiliser l'expression suivante pour l'allocation dynamique de tableaux à plusieurs dimensions:

pointeur=new type[taille1][taille2]...;

L'allocation dynamique de tableaux à deux dimensions est expliquée ci-dessous.

Les vectors

Les vectors (vecteur en français) sont un type de tableaux dynamiques très puissants qui suivent la syntaxe suivante :

vector<type> nom(taille);

Par exemple, un tableau dynamique constitué de 5 entiers et nommé « tableau_nombre_entiers » sera défini de la sorte :

vector<int> tableau_nombres_entiers(5);

Mais on peut bien sûr définir un tableau qui ne comporte aucune taille, ce qui est bien utile pour un tableau... dynamique !

vector<int> tableau_nombres;

Pour accéder aux valeurs d'un vector, on procède la même manière que pour tous les tableaux, par exemple :

tableau_nombres_entiers[0] = 0;
tableau_nombres_entiers[1] = 2;
...

Mais on peut également affecter à tout le vector une même valeur en l'indiquant juste après la taille avec une virgule. Exemple :

#include <iostream>
#include <string> //ATTENTION : PENSER À INCLURE "STRING"

using namespace std;

vector<int> tableau_nombre_entiers(5, 10); //Ce tableau est composé de 5 nombres entiers qui sont tous égaux à 10
vector<string> tableau_lettres(4, "bonjour !"); //Ce tableau est composé de 4 chaînes de caratères qui sont égales à « bonjour ! »

Cela signifie que tableau_nombre_entiers[0]=tableau_nombre_entiers[1]=tableau_nombre_entiers[2]=tableau_nombre_entiers[3]=tableau_nombre_entiers[4]=10 ; et de même tableau_lettres[0]=tableau_lettres[1]=tableau_lettres[2]=tableau_lettres[3]="bonjour !".

Comme on le sait, les vectors sont des tableaux dynamiques, ainsi peuvent-ils s'agrandir ou se rétrécir. Pour ajouter une case supplémentaire au tableau, il suffit de faire appel à la fonction push_back() :

vector<int> tableau_entiers;
tableau_entiers.push_back(7); //On ajoute une première case au tableau qui comporte le nombre 7
tableau_entiers.push_back(18); //On ajoute une deuxième case au tableau qui comporte le nombre 18
...

À l'inverse, pour supprimer une case, on utilise la fonction pop_back() :

vector<int> tableau_entiers(10); //Un tableau dynamique d'entiers à 10 cases
tableau_entiers.pop_back(); //Plus que 9 cases
tableau_entiers.pop_back(); //Plus que 8 cases
...

Enfin, pour récupérer la taille du vector, on utilise la fonction size() :

vector<int> tableau_entiers(5); //Un tableau de 5 entiers
int taille = tableau_entiers.size(); //'taille' a pour valeur 5

Parcours d'un tableau

Le parcours des éléments d'un tableau se fait généralement avec une boucle for (on peut aussi utiliser while ou do...while). Exemple :

int nombres[10] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 };
for (int i = 0; i < 10; i++)  // i va de 0 à 9 inclus
    cout << "nombres [ " << i << " ] = " << nombres[i] << endl;

Les éléments d'un tableau sont stockés dans des zones contigües. Il est donc possible d'utiliser un pointeur pour parcourir un tableau :

int nombres[10] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 };
int *p = nombres;
for (int i = 0; i < 10; i++, p++)
    cout << "Nombre : " << *p << endl;

Initialement le pointeur p pointe le premier élément du tableau. L'instruction p++ dans la boucle for incrémente le pointeur, c'est-à-dire qu'il passe à l'élément suivant. L'adresse contenue dans le pointeur n'augmente pas de 1, mais de la taille de l'élément pointé (ici int, soit 4 octets en général).

Les tableaux à deux dimensions

Statiques

  • Syntaxe

type identificateur[taille_i][taille_j];

  • Sémantique :

identificateur est un tableau de taille_i sur taille_j cases. Pour chaque case i choisie, il y a taille_j cases disponibles. Les taille_i et taille_j ne peuvent changer au cours du programme. Pour le faire, il faut utiliser les tableaux dynamiques.

Exemple

#include <iostream>
using namespace std;

int main() {
  int t[10][5];
  
  for (int i = 0; i < 10; i++)
    for (int j = 0; j < 5; j++)
      t[i][j] = i * j;
  
  for (int i = 0; i < 10; ++i) {
    for (int j = 0; j < 5; ++j){
     cout << t[i][j] << " ";
     }
    cout << endl;
  }
     
  return 0;
}

Exécution

0 0 0  0  0
0 1 2  3  4
0 2 4  6  8
0 3 6  9  12
0 4 8  12 16
0 5 10 15 20
0 6 12 18 24
0 7 14 21 28
0 8 16 24 32
0 9 18 27 36

Dynamiques

Exemple

#include <iostream>
using std::cout;
using std::cin;

int **t;
int nColonnes;
int nLignes;
 
void Free_Tab();  // Libérer l'espace mémoire;
 
int main(){
 
  cout << "Nombre de colonnes : ";  cin >> nColonnes;
  cout << "Nombre de lignes : ";  cin >> nLignes;
 
  /* Allocation dynamique */
  t = new int* [ nLignes ];
  for (int i=0; i < nLignes; i++)
    t[i] = new int[ nColonnes ];
 
  /* Initialisation */
  for (int i=0; i < nLignes; i++)
    for (int j=0; j < nColonnes; j++)
      t[i][j] = i*j;
 
  /* Affichage */
  for (int i=0; i < nLignes; i++) {
 
    for (int j=0; j < nColonnes; j++)
      cout << t[i][j] << " ";
    cout << std::endl;
  }
 
  Free_Tab();
  system("pause>nul");
  return 0;
}
 
void Free_Tab(){
 
  for (int i=0; i < nLignes; i++)
    delete[] t[i];
  delete[] t;
}

Exécution 1

Nombre de colonnes : 6
Nombre de lignes : 3
0 0 0 0 0 0
0 1 2 3 4 5
0 2 4 6 8 10

Exécution 2

Tapez la valeur de taille_i : 8
Tapez la valeur de taille_j : 2
0 0
0 1
0 2
0 3
0 4
0 5
0 6
0 7

Les tableaux de caractères

Un tableau de caractères constitue une chaîne de caractères. Exemple avec des caractères de huit bits:

char chaine[20] = "BONJOUR";


Les tableaux de char

Un tableau de char constitue une chaîne de caractères. Exemple :

char chaine[20] = "BONJOUR";

La variable chaine peut stocker jusqu'à 20 caractères, et contient les caractères suivants :

'B','O','N','J','O','U','R','\0'

Le dernier caractère est nul pour indiquer la fin de la chaîne de caractères. Il est donc important de prévoir son stockage dans le tableau. Dans cet exemple, 8 caractères sont donc nécessaires.

L'exemple précédent équivaut à :

char chaine[20] = {'B','O','N','J','O','U','R','\0'};

Si la chaîne est initialisée à la déclaration, on peut également laisser le compilateur détecter le nombre de caractères nécessaires, en omettant la dimension :

char chaine[] = "BONJOUR"; // tableau de 8 caractères

Dans ce cas la variable ne pourra stocker que des chaînes de taille inférieure ou égale à celle d'initialisation.

Les fonctions de manipulation de chaîne de caractères sont les mêmes que dans le langage C. Elles portent un nom commençant par str (String) :

int strlen(const char* source)
Retourne la longueur de la chaîne source (sans le caractère nul final).
void strcpy(char* dest, const char* source)
Copie la chaîne source dans le tableau pointé par dest.

Les tableaux de char16_t and char32_t

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

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

Il est également possible de les initialiser avec des littéraux, selon le cas:

u8"I'm a UTF-8 string."
u"This is a UTF-16 string."
U"This is a UTF-32 string."

Les tableaux de wchar_t

Les compilateurs moderne permettent de faire des chaînes de wchar_t (16 bits ou 32 bits) au lieu de char (8 bits). Ceci a été fait pour permettre de représenter une plus grande partie des caractères Unicode, même si on peut représenter l'ensemble de ces caractères en UTF-8.

Dans ce cas, il faut précéder les caractères par L :

wchar_t chaine[20] = L"BONJOUR";

Equivaut à :

wchar_t chaine[20] = {L'B',L'O',L'N',L'J',L'O',L'U',L'R',L'\0'};

Les fonctions permettant leur manipulation portent un nom similaire, excepté que str doit être remplacé par wcs (Wide Character String) :

  • int wcslen(const wchar_t* source)
  • wcscpy(wchar_t* dest, const wchar_t* source)

Portabilité

Certains environnements de développement (Visual C++ en particulier) considèrent Unicode comme une option de compilation. Cette option de compilation définit alors le type TCHAR comme char (option legacy) ou wchar_t (option unicode), selon l'option Unicode. Les constantes sont alors à encadrer par la macro _T(x) qui ajoutera L si nécessaire :

TCHAR chaine1[20] = _T("BONJOUR");
TCHAR chaine2[20] =
{_T('B'),_T('O'),_T('N'),_T('J'),_T('O'),_T('U'),_T('R'),_T('\0')};

Le nom des fonctions de manipulation commencent par _tcs :

  • int _tcslen(const TCHAR* source)
  • _tcscpy(TCHAR* dest, const TCHAR* source)

Voir aussi