char, int ou double), d'une structure ou une union, de pointeurs ou de tableaux.
int tab1[5];
définit un tableau de nom tab1 de 5 entiers de type int.
Les cinq éléments du tableau sont identifiables individuellement par
tab1[0],
tab1[1],
tab1[2],
tab1[3] et
tab1[4].
struct point tabPoint[3];
définit un tableau de trois éléments dont chacun a la structure point,
supposée définie préalablement par exemple par
struct point {float abs; float ord;};;
Les trois éléments du tableau sont identifiables individuellement par tabPoint[0],
tabPoint[1] et tabPoint[2].
i (0, 1 ou 2), « tabPoint[i].abs »
et « tabPoint[i].ord » identifieront les deux champs
du point en position i dans le tableau.
char tab2[4][3];
définit un tableau de nom tab2 de 4 tableaux de 3 caractères :
tab2[0], tab2[1], tab1[2] et tab2[3] sont des tableaux de 3 caractères;tab2[0][0], tab2[0][1] et
tab2[0][2] désignent les 3 éléments de type char du tableau
tab2[0].[entier_positif]; ».
Comme toute définition de variable, la définition d'un tableau vise à associer
de la mémoire à une variable (et donc d'allouer un espace en mémoire).
int tt[ ];
provoque lors de la compilation un message d'avertissement (warning)
qui n'empêche cependant pas la construction d'un binaire exécutable :
warning: array 'tt' assumed to have one element
Le type spécifié pour tt est ce qu'on appelle un type incomplet : il ne contient pas suffisamment
d'information pour en déterminer la taille (un autre exemple,
de nature un peu différente, de type incomplet est void).
Par contre, une tentative d'accès à la taille du tableau dans le programme avec l'opérateur sizeof provoquerait
une véritable erreur de compilation sans construction d'exécutable :
error: invalid application of 'sizeof' to incomplete type 'int[ ]'
Remarque :
si on reprend la définition du tableau tt avec le type incomplet, un programme
dans lequel on écrirait ensuite :
tt[10] = 10;
se compile sans aucun problème et s'éxécute le cas échéant (cela peut dépendre des systèmes et des machines). Dans ce
cas, inutile de préciser que des données ont du être malencontreusement écrasées et que les résultats obtenus au bout
du compte sont peu fiables!
▶ La définition d'un tableau peut être accompagnée de son initialisation comme dans les définitions suivantes :
int t[ ] = {1, 2, 3};
char c1[10] = {'a', 'b', 'c', 'd'};
/* assez warning: excess elements in array initializer */
char c2[2] = {'a', 'b', 'c', 'd'};
int tt[ ][4] = {{1,2},{1,3,5},{5},{1,5,7,8}};
Il est possible de définir un type de « tableau de quelque chose » en utilisant
typedef.
typedef int tab10int[10];
définit un type de nom tab10int correspondant à des tableaux de 10 entiers.
La définition « tab10int t; » définit alors un tableau t de 10 entiers.
sizeof donne la taille
totale de l'espace occupé par l'ensemble des éléments du tableau et donc pour nos exemples :
sizeof(tab1) est égal à « 5 * sizeof(int) » : pour une taille 4 de int,
on obtient 20;sizeof(tabPoint) est égal à « 3 * sizeof(struct point) » :
pour la structure point que nous avons proposée dont la taille est 8, on obtient
24 comme taille globale;sizeof(tab2) est égal à « 4 * 3 * sizeof(char) », et donc 12 comme
taille globale.▶ Les différents éléments d'un tableau sont implantés de manière contigue en mémoire.
▶ Sit est un tableau d'éléments de
type T, le premier élément, désignable par
l'expression t[0],
occupe les sizeof(T) premiers
octets alloués au tableau, le deuxième élément désignable par t[1]
occupe les sizeof(T) octets suivants, ...
▶ L'indexation d'un tableau
permettant d'accéder aux éléments d'un tableau
est réalisée entre crochets [ et ];
▶ L'indexation commence à 0.
▶ Insistons sur le fait qu'en C aucun contrôle n'est réalisé lors de l'accès à un tableau par indexation, contrairement à ce
qui se passe en Java par exemple.
Pour l'expliquer il faut savoir (et cela sera repris en détail plus loin) que
--> cat adresse_et_tableau.c
#include <stdio.h>
#include <stdlib.h>
int main() {
int j;
int i = 100;
int tab[3] = {10, 20, 30};
printf("%p %p\n", tab+3, &i);
for(j = 0; j <= 3; j++)
printf("%d ", tab[j]);
putchar('\n');
return EXIT_SUCCESS;
}
--> ./adresse_et_tableau
0xbffffa38 0xbffffa38
10 20 30 100
-->
t est un tableau à deux dimensions
d'éléments de type T (c'est-à-dire un tableau de tableaux d'éléments de type T),
déclaré sous la forme :
T t[n1][n2];
t[0][0], t[0][1], ..., t[0][n2-1],t[1][0],t[1][1],..., t[1][n2-1], t[2][0]..., t[n1-1][n2-1]
Si on assimile le premier indice à un indice de ligne et le second
à un indice de colonne, on peut dire que les tableaux à
deux dimensions (tableaux de tableaux) sont rangés lignes par lignes.
Il y a des circonstances où on est amené à utiliser des tableaux dont on ne connaît pas a priori la taille et donc déclarés (et non définis) localement au travers de types incomplets.
▶ On peut citer comme exemples :extern comme par exemple :
extern int t[ ];
Cette déclaration indique qu'on souhaite utiliser un tableau de nom t d'entiers supposé défini ailleurs.int fonc(int t1[ ], int t2[ ])
Le prototype d'une telle fonction (typiquement défini dans un fichier d'en-tête) est :
int fonc(int [ ], int [ ]);
void. La taille du tableau n'est pas nécessaire.
Une déclaration d'un paramère tableau d'éléments de type T sous la forme « T t[ ] » est donc possible;
t de n1 tableaux de n2
entiers dont la définition est « int t[n1][n2]; »
t[i] est un tableau de n2 entiers.
Pour l'atteindre en mémoire il faut se déplacer depuis le début du tableau
(c'est-à-dire la constante t ou &t[0][0])
de i fois la taille d'un tableau de n2 entiers;t[i][j] du tableau,
il faut d'abord se déplacer sur t[i] (c'est-à-dire sur
t[i][0]), puis dans ce tableau se déplacer de j positions.int t[ ][n2] »
est possible.
int t[ ][n2]...[np] »
t[i] » est traduit en « *(t + i) »int t[] » et « int *t »
sont équivalentes, dans tous les autres contextes tableau et pointeur sont « quelque part » différents.
▶ Les principales différences qu'on peut voir entre ces deux concepts se résument à ce que :
NULL s'il est en zone statique).
malloc++ et --.T tab[N];
tab de N
(expression constante) éléments de type T
correspond à
N
valeurs de type T.
sizeof(tab)
est « N * sizeor(T) ».
staticauto)tab sur le type T ayant pour valeur l'adresse en mémoire du premier élément du tableau.
tab sont converties en manipulation de
« pointeur sur T ».char tab1[ ] = "Bonjour";
char *tab2 = "Bonjour";
--> cat tableau_chaine.c
#include <stdio.h>
#include <stdlib.h>
char tab1[ ] = "Bonjour";
char *p1 = "Bonjour";
int main( ) {
char tab2[ ] = "Bonjour";;
char *p2 = "Bonjour";
printf("tab1 :\n");
printf(" taille : %ld\n", sizeof(tab1));
printf(" adresse : %p\n", &tab1);
printf(" valeur : %p\n", tab1);
printf("p1 :\n");
printf(" taille : %ld\n", sizeof(p1));
printf(" adresse : %p\n", &p1);
printf(" valeur : %p\n", p1);
printf("tab2 :\n");
printf(" taille : %ld\n", sizeof(tab2));
printf(" adresse : %p\n", &tab2);
printf(" valeur : %p\n", tab2);
printf("p2 :\n");
printf(" taille : %ld\n", sizeof(p2));
printf(" adresse : %p\n", &p2);
printf(" valeur : %p\n", p2);
return EXIT_SUCCESS; // return 0;
}
--> ./tableau_chaine
tab1 :
taille : 8
adresse : 0x2014
valeur : 0x2014
p1 :
taille : 4
adresse : 0x201c
valeur : 0x1fa6
tab2 :
taille : 8
adresse : 0xbffffad8
valeur : 0xbffffad8
p2 :
taille : 4
adresse : 0xbffffad4
valeur : 0x1fa6
-->