Les tableaux, les pointeurs et les chaînes de style C
Tableau à un indice
Un tableau à un indice est 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 dans l’ensemble (le premier élément est repéré par l’indice 0). L’instruction suivante : float t [10] ; réserve l’emplacement pour un tableau de 10 éléments de type float, nommé t. Un élément de tableau est une lvalue. Un indice peut prendre la forme de n’importe quelle expression arithmétique d’un type entier quelconque. Le nombre d’éléments d’un tableau (dimension) doit être indiqué sous forme d’une expression constante. exos_c++.book Page 49 Jeudi, 5. juillet 2007 11:10 11 Les tableaux, les pointeurs et les chaînes de style C Exercices en langage C++ 50 © Éditions Eyrolles Tableaux à plusieurs indices La déclaration : int t[5][3] ; réserve un tableau de 15 (5 × 3) éléments. Un élément quelconque de ce tableau se trouve alors repéré par deux indices comme dans ces notations : t[3][2] t[i][j] D’une manière générale, on peut définir des tableaux comportant un nombre quelconque d’indices. Les éléments d’un tableau sont rangés en mémoire selon l’ordre obtenu en faisant varier le dernier indice en premier.
Initialisation des tableaux
Les tableaux de classe statique sont, par défaut, initialisés à zéro. Les tableaux de classe automatique ne sont pas initialisés par défaut. On peut initialiser (partiellement ou totalement) un tableau lors de sa déclaration, comme dans ces exemples : int t1[5] = { 10, 20, 5, 0, 3 } ; // place les valeurs 10, 20, 5, 0 et 3 dans les cinq éléments de t1 int t2 [5] = { 10, 20 } ; // place les valeurs 10 et 20 dans les deux premiers éléments de t2 Les deux déclarations suivantes sont équivalentes : int tab [3] [4] = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9,10,11,12 } } int tab [3] [4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 } ; Les initialiseurs fournis pour l’initialisation des éléments d’un tableau doivent être des expressions constantes pour les tableaux de classe statique, et des expressions quelconques (d’un type compatible par affectation) pour les tableaux de classe automatique.
Les pointeurs, les opérateurs & et *
Une variable pointeur est destinée à manipuler des adresses d’informations d’un type donné. On la déclare comme dans ces exemples : int * adi ; // adi contiendra des adresses d’entiers de type int float * adf ; // adf contiendra des adresses de flottants de type float exos_c++.book Page 50 Jeudi, 5. juillet 2007 11:10 11 © Éditions Eyrolles 51 chapitre n° 4 Les tableaux, les pointeurs et les chaînes de style C L’opérateur & permet d’obtenir l’adresse d’une variable : int n ; ….. adi = &n ; // adi contient l’adresse de n L’opérateur * permet d’obtenir l’information d’adresse donnée. Ainsi *adi désigne la lvalue d’adresse adi, c’est-à-dire ici n : int p = *adi ; // place dans p la valeur de n *adi = 40 ; // place la valeur 40 à l’adresse contenue dans adi, // donc ici dans n Il existe un type « pointeur générique », c’est-à-dire pour lequel on ne précise pas la nature des informations pointées, à savoir le type void *.
Opérations sur les pointeurs
On peut incrémenter ou décrémenter des pointeurs d’une valeur donnée. Par exemple : adi++ ; // modifie adi de manière qu’elle pointe // sur l’entier suivant n adi -= 10 ; // modifie adi de manière qu’elle pointe // 10 entiers avant L’unité d’incrémentation ou de décrémentation des pointeurs génériques est l’octet. On peut comparer ou soustraire des pointeurs de même type (pointant sur des éléments de même type).
Affectations de pointeurs et conversions
Il n’existe aucune conversion implicite d’un type pointeur en un autre, à l’exception de la conversion en pointeur générique. Il n’existe aucune conversion implicite d’un entier en un pointeur, à l’exception de la valeur 0 qui est alors convertie en un pointeur ne « pointant sur rien ».
Tableaux et pointeurs
Un nom de tableau est une constante pointeur. Avec : int t[10] ; t+1 est équivalent à &t[1] ; t+i est équivalent à &t[i] ; ti] est équivalent à *(t+i). exos_c++.book Page 51 Jeudi, 5. juillet 2007 11:10 11 Exercices en langage C++ 52 © Éditions Eyrolles Avec : int t[3] [4] ; t est équivalent à &t[0][0] ou à t[0] ; t+2 est équivalent à &t[2][0] ou à t[2] Lorsqu’un nom de tableau est utilisé en argument effectif, c’est (la valeur de) l’adresse qui est transmise à la fonction. La notion de transmission par référence n’a pas de sens dans ce cas. Dans la déclaration d’une fonction disposant d’un argument de type tableau, on peut utiliser indifféremment le formalisme « tableau » (avec ou sans la dimension effective du tableau) ou le formalisme pointeur ; ces trois déclarations sont équivalentes : void fct (int t[10]) void fct (int t[]) void fct (int * t)
Gestion dynamique de la mémoire
Si type représente la description d’un type absolument quelconque et si n représente une expression d’un type entier (généralement long ou unsigned long), l’expression : new type [n] alloue l’emplacement nécessaire pour n éléments du type indiqué et fournit en résultat un pointeur (de type type *) sur le premier élément. En cas d’échec d’allocation, il y a déclenchement d’une exception bad_alloc (les exceptions font l’objet du chapitre 19). L’indication n est facultative : avec new type, on obtient un emplacement pour un élément du type indiqué, comme si l’on avait écrit new type[1]. L’expression : delete adresse // il existe une autre syntaxe pour les // tableaux d’objets – voir chap. 9 libère un emplacement préalablement alloué par new à l’adresse indiquée. Il n’est pas nécessaire de répéter le nombre d’éléments, du moins lorsqu’il ne s’agit pas d’objets, même si celuici est différent de 1. Le cas des tableaux d’objets est examiné au chapitre 9.