Programmation C++/Les tableaux
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]
où 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é pardest
.
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)