VI. Objets tableaux▲
Un objet tableau est un ensemble d'objets, tous du
même type et placés consécutivement en mémoire. Céer un
objet tableau revient à créer les objets qui le constituent.
L'initialisation des objets dépend du mode de création du
tableau (Voir
Vie et mort des objets)
Il est possible de créer un objet tableau ne comportant qu'un
objet, mais le comportement de cet objet tableau sera différent
de celui de l'objet qu'il contient. On ne peut créer de tableau
vide, c'est à dire ne comportant aucun objet.
Rappelons que les objets tableaux sont les seuls qui ont un type différent de celui de leur valeur. Voir Valeur d'un objet
VI-A. Type tableau▲
La notation [] permet d'obtenir les éléments d'un tableau. Pour écrire le type Tt d'un tableau de N objets de type T, en cohérence avec la signification de cet opérateur, on écrira que Tt [N] est un objet de type T :
T Tt [N] |
Les objets de type T peuvent être éventuellement eux-mêmes des tableaux de M objets de type T'. Le tableau est alors à plusieurs dimensions. On écrira alors suivant le même principe que Tt [N] est un tableau d'objets T' :
T' (Tt [N] ) [M] |
ou, comme les parenthèses sont ici inutiles,
T'
Tt [N] [M]
|
ce qui pourra être interprété au choix comme :
- T'
Tt [N] [M] , autrement
dit "Tt est tableau de N
tableaux de M objets T' "
- T'
Tt [N] [M] soit
"Tt [] est tableau de M
objets T' "
- T' Tt [N] [M] soit "Tt [] [] est un objet T' "
VI-B. Opérateurs définis pour les tableaux▲
Les seuls opérateurs définis pour un tableau sont l'
opérateur unaire
& (adresse de) et
sizeof.
Il n'y a donc aucune possibilité d'effectuer des traitements
sur un tableau (dans son ensemble).
Pour manipuler l'information contenue dans un tableau,
il faut traiter individuellement les éléments du tableau
avec les opérateurs qui leurs sont propres.
-
L'opérateur &
donne l'adresse du tableau (qui
correspond à l'adresse du début du tableau).
Le type Ta de cette valeur est "adresse d'un tableau de N objets de type T ". On écrira donc que (*Ta) est un tableau de N objets de type T :
T (*Ta) [N] |
Les parenthèses sont obligatoires ici : En leur absence,
la priorité des opérateurs donnerait le groupement
T * (Ta [N]) ce
qui définirait un tableau de pointeurs sur T.
- L'opérateur sizeof donne le nombre de bytes occupés en mémoire par le tableau. On a donc, puisque les objets sont consécutifs en mémoire, sizeof (Tt) qui est égal à N*sizeof (T).
Soit tab, déclaré par T tab [N].
- Alors sizeof tab
est égal à
N*sizeof (T).
- Mais si tab est dans un contexte de valeur, comme dans
sizeof (tab+0), le
résultat est égal à
sizeof (T*).
(Voir
Valeur d'un objet tableau)
VI-C. Valeur d'un objet tableau▲
Si un objet "tableau d'objets de type T" a été créé,
la valeur de l'objet tableau est l'adresse du
premier objet du tableau. Les opérateurs s'appliquant à la
valeur d'un objet tableau sont donc les
opérateurs s'appliquant aux adresses.
- Le type Tv de cette valeur étant
"adresse d'un objet de type T" , on a :
T * Tv |
Elle est donc différente du type Tt du tableau : T Tt [N] et du type Ta de l'adresse du tableau : T (*Ta) [N]).
La connaissance du contexte dans lequel est placé l'identificateur du tableau est donc primordiale : Dans un contexte d'objet, il est du type T [N] et dans un contexte de valeur du type T * .
- Si le type Tt du tableau est T' Tt [N] [M], alors le type Tv de sa valeur sera "adresse d'un tableau de M objets de type T' " :
T' (*Tv) [M] |
-
La valeur de l'objet tableau indique où se trouvent
les éléments du tableau, mais pas leur nombre. Le nombre
d'éléments du tableau est déterminé à sa création et si
on en a besoin ultérieurement (ce qui est fréquent), il
faudra prendre la précaution de le sauvegarder.
-
La valeur d'un objet tableau est non modifiable,
autrement dit, les opérateurs
=, +=, ... ,++ et --
ne sont pas définis pour les tableaux et il est
impossible d'écrire tableau = ...
(Voir Valeur d'un objet)
VI-D. Fonctions et tableaux▲
Puisque les arguments d'une fonction sont dans un contexte
de valeur, on ne peut pas passer en argument d'une fonction
un objet mais seulement la valeur d'un objet.
Dans le cas d'un objet tableau, l'argument sera donc la
valeur du tableau soit l'adresse de son
premier élément. Si il s'agit d'un tableau d'objets de type
T, le paramètre correspondant de la fonction
sera donc du type T*.
Le langage tolère également de déclarer le paramètre dans la
liste des paramètres de la fonction sous la forme tableau :
T[]. Le choix de T* montre bien que la
variable locale à la fonction est un pointeur (ce qui est
vrai dans les deux cas) alors que la notation tableau
T[] insiste sur le fait que l'argument passé à la
fonction est l'adresse du début d'un tableau et non pas
celle d'un objet "simple".
exemple Sélectionnez
|
La fonction ne peut connaître le nombre d'éléments du
tableau sauf si celui-ci est passé en argument ou si il
peut se déduire du contenu du tableau. Ce dernier cas est
celui des chaînes de caractères où la valeur '\0'
est présente comme sentinelle pour indiquer la fin du
tableau.
Dans le cas des tableaux à plusieurs dimensions, le problème
se complique : Si le tableau est déclaré comme :
T' Tab [N] [M] |
le type Tv de sa valeur, qui doit
être le type du paramètre correspondant dans la fonction,
est
T' (*Tv) [M] |
Le problème majeur est la présence de la valeur
M dans la définition de ce type : Il est nécessaire
que le compilateur connaisse cette valeur sinon, il ne
connaît pas la taille des éléments du tableau et ne peut
donc calculer leur position. Ceci est une énorme contrainte
puisqu'une fonction, écrite pour une valeur donnée de
M ne peut être utilisée pour une autre valeur
(à moins d'utiliser des tableaux de longueur variable
(VLA) du C99 ).
exemple Sélectionnez
|
Finalement, les tableaux à plusieurs dimensions sont très
contraignants pour les fonctions et on préfèrera souvent
les concevoir comme un ensemble de tableaux à une dimension, très souples à utiliser,
au prix d'une occupation mémoire augmentée et du code supplémentaire
nécessaire à leur création. Pour créer un tableau à deux dimensions,
on créera un tableau (à une dimension) de pointeurs, chaque pointeur
contenant l'adresse d'un tableau (à une dimension) représentant
une "ligne" du tableau à deux dimensions.
Le même principe est applicable pour un nombre de dimensions plus grand.
L'expression suivant un
return
étant dans un contexte de valeur, il n'est pas possible à
une fonction de retourner un tableau mais uniquement la
valeur du tableau, donc l'adresse de son
premier élément.
On se souviendra qu'il est interdit de retourner l'adresse
d'un objet local, tableau ou non, car, étant en
allocation automatique,
l'objet est détruit en sortie de la fonction et
on renvoie dans ce cas l'adresse d'un objet "mort".