Next: 7 Pointeurs
Up: Le langage C
Previous: 5 Classification des données
alloue en mémoire un zone capable de contenir 5 éléments de type type i.e. 5 * sizeof(type). Les éléments du tableau sont désignés par tab[0], tab[1], tab[2], tab[3] et tab[4].
type tab[5];
Dans le langage C, le premier élément d'un tableau est d'indice 0 et non pas 1 !!!
Le nombre d'éléments d'un tableau (5 dans l'exemple 6.1.1) peut être spécifié par une expression à condition qu'à la compilation on puisse connaître sa valeur.
Il y a deux cas où la taille du tableau est omise:
Le nom d'un tableau est équivalent à une constante et peut figurer dans n'importe quelle expression où une constante est permise. Il joue également le même rôle que l'adresse du premier élément du tableau. Les deux expressions
sont identiques sauf dans les deux cas:
t et &t[0]
Tout ceci donne bien la philosophie de l'implantation des tableaux dans le langage C. Excepté au moment de la déclaration et du calcul de la taille, le langage C ignore complètement l'espace mémoire occupé par un tableau: il en découle qu'il appartient au programmeur de s'assurer qu'il ne déborde pas du tableau; il n'y a aucun test de débordement pouvant servir de garde-fou pour le programmeur. Dans l'exemple 6.1.1 du tableau de 5 éléments, on peut tout à fait accéder au dixième élément (ficif) tab[10], aux risques et périls du programme.
Le débordement du tableau fait partie des erreurs de programmation fréquentes et dramatiques.
Comme nous l'avons déjà dit, le nom d'un tableau doit être considéré comme une constante; ce n'est pas une lvalue. Si tab1 et tab2 sont deux tableaux, l'affectation tab1 = tab2 produira une erreur de compilation car tab1 n'est pas modifiable.
Comme toute variable, une fois définie, un tableau doit être initialisé. La première manière de le faire est d'écrire une fonction d'initialisation :
Le langage C permet également l'initialisation d'un tableau à la défintion de celui-ci. Cette initialisation se fait à la définition et donc les valeurs utilisées doivent être des constantes (sous forme d'expression si nécessaire).
#define NBRE_ELTS 10 voir chapitre 9 #define MAX_INDICE NBRE_ELTS - 1int tab[NBRE_ELTS];
...
for ( i = 0; i <= MAX_INDICE; i++) boucle d'initialisation tab[i] = ... valeur quelconque ...
#define NBRE_ELTS 10 voir chapitre 9 #define MAX_INDICE NBRE_ELTS - 1int tab1[NBRE_ELTS] = {0,1,2,3,4,5,6,7,8,9}; initialisation des 10 éléments int tab2[NBRE_ELTS] = {0}; initialisation du seul premier élément int tab3[NBRE_ELTS] = {0,1,2,3,4,5,6,7,8,9,10,11}; Warning de compilation tableau à 1 éléments int tab3[] = {0,1,2,3,4,5,6,7,8,9,10,11}; tableau à 12 éléments
Dans la déclaration d'un argument formel t de type tableau:
Voici deux versions d'une même fonction:
int strlen(char chaine[]) { register int i=0; while (chaine[i] != 0) i++; return i; }
int strlen(char *chaine) { register int i=0; while (chaine[i] != 0) i++; return i; }
int strlen(char *chaine) { register int i=0; while (*(chaine+i) != 0) i++; return i; }
Il n'est pas possible, de manière simple, de passer par valeur un tableau.
De même, lorque le retour d'une fonction est un tableau, ce qui est transmis, ce n'est pas le tableau tout entier mais uniquement l'adresse du premier élément du tableau. Il faut alors s'assurer que le tableau transmis n'est pas un tableau local qui disparaît à la fin de fonction appelée.
tab * fonc(void) { int tab[30]; ... return tab; ce tableau n'aura plus d'existence } à la fin de cette fonction
Une fonction ne doit jamais retourner un tableau local.
Nous verrons dans la section qui suit une astuce pour contourner ce problème.
Les tableaux à plusieurs dimensions sont des tableaux dont les éléments sont eux même des tableaux (par exemple des matrices). La déclaration d'un tableau multi-dimentionnel est comme suit:
Par exemple, une matrice de dimension 4 * 4 se définit comme suit:
type nom[index1][index2] ... [indexN]; type nom[index1][index2] ... [indexN] = { ... } ;
A une telle matrice est associée une zone mémoire de 4 * 4 * sizeof(int) octets consécutifs; de façon interne, une matrice est représentée de manière linéaire. L'élément d'indice ( i , j ) de la matrice se note matrice[i][j]
int matrice[4][4]
L'initialisation de cette matrice peut se faire de deux manières:
int matrice1[4][4] = {1,2,3,4,5,6,7,9};
1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 |
A9 | |||
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
int matrice2[4][4] = { {1,2,3}, {4,5,6}, {7,8,9} };
1 | 2 | 3 | |
4 | 5 | 6 | |
7 | 8 | 9 | |
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
Voici pour exemple, la fonction extraite de la bibliothèque standard qui calcule la longueur d'une chaîne de caractères:
int strlen(char s[]) { int i = 0; while (s[i] != '\0') i=i+1; return i; }
Et l'on dispose de racourcis agréables:
char chaine1[100] = {'c', 'o', 'u', 'c', 'o', 'u'}; initialisation des 6 premiers caractères char chaine2[] = {'c', 'o', 'u', 'c', 'o', 'u'}; chaîne de longueur 6 (+1 pour coder la fin de chaine)
char chaine1[100] = "coucou"; initialisation des 6 premiers caractères char chaine2[] = "coucou"; chaîne de longueur 6 (+1 pour coder la fin de chaine)
Voici un premier exemple de définition d'une structure:
Les trois variables a, b et c sont définies de type struct fiche. Ce type est constitué d'un numéro (entier), d'un nom (chaîne de caractère) et d'une ville (chaîne de caractère). Dans la suite du programme, il est désormais possible de définir des variables de type struct fiche tout comme n'importe quel type prédéfini.
struct fiche { int numero; char nom[32]; char ville[32]; } a, b, c;
Si l'on ne désire pas définir d'autres variables de type struct fiche, on peut omettre de nommer la structure.
struct fiche x, y;
struct { int numero; char nom[32]; char ville[32]; } a, b, c, x, y;
{ ... a.numero = 124; a.nom = "Touraivane"; a.ville = "Marseille"; ... }
Le passage par valeur d'une structure consiste à passer chacune de ses champs. Le retour d'une fonction peut être une structure. Nous verrons ces notions plus en détail dans le chapitre 4.
a.numéro
a.nom (32 octets)
a.ville (32 octets)
struct fiche { int numero; char nom[32]; char ville[32]; } a = { 124, "Touraivane", "Marseille" }, b = { 125, "Mondain-Monval", "Marseille" };
Les champs des stuctures se suivent sans se chavaucher alors que les champs des unions ont la particularité de se chauvaucher. Il s'agit d'un moyen de ``voir'' une donnée sous de multiples aspects.
La variable peut être vue comme un unsigned long ou comme un pointeur vers un caractère (char *). Ainsi, a.ulong et a.ptr désigne respectivement l'aspect unsigned long et char * de la variable a.
union mot { unsigned long ulong; char *ptr; } a;
Les champs d'une union ne sont pas forcément de même taille; la taille d'une union est alors la taille du plus volumineux de ces champs.
Une astuce pour transmettre un tableau par valeur consiste à l'encapsuler dans une structure.
struct capsule int tab[100];void modifier(struct capsule s) { s.tab[1] = 999; }
void main(void) { struct capsule c; c.tab[1]=0; modifier(c); printf("%d", c.tab[1]); La valeur affichée sera 0: c'est bien un passage par valeur }
Le langage C permet de définir des structures dont les champs ne sont pas des objets de types élémentaires ou structurés. Il s'agit des champs de bits i.e. une décompostion plus fine de la structure:
struct mot { unsigned c1 : 4 ; unsigned c2 : 12 ; : 4 ; unsigned c3 : 12 ; }
Comme pour les struct et les union, il est possible de définir d'autres variables de ce type de la manière suivante :
enum jour {lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche} x;
Encore un fois, comme pour les struct et les union, le nom de l'énumaration n'est utile que si l'on désire définir par la suite d'autre variables de ce même type.
enum jour y, z;
Les noms symboliques que l'on se donne dans la définition d'une énumération sont en correspondance parfaite avec des entiers; ici lundi correspond à 0, mardi correspond à 1, etc ...
enum {lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche} x, y, z;
On peut toutefois altérer cette correspondance de manière explicite :
et lundi correspond à 3, mardi correspond à 4, mercredi correspond à 5, jeudi correspond à 8, vendredi correspond à 9, etc ...
enum {lundi=3, mardi, mercredi, jeudi=8, vendredi, samedi, dimanche} x, y, z;
Touraivane