Les Tableaux - Le Langage C • Tutoriels • Zeste De Savoir
Maybe your like
Définition
La définition d’un tableau nécessite trois informations :
- le type des éléments du tableau (rappelez-vous : un tableau est une suite de données de même type) ;
- le nom du tableau (en d’autres mots, son identificateur) ;
- la longueur du tableau (autrement dit, le nombre d’éléments qui le composent). Cette dernière doit être une expression entière.
Comme vous le voyez, la syntaxe de la déclaration d’un tableau est similaire à celle d’une variable, la seule différence étant qu’il est nécessaire de préciser le nombre d’éléments entre crochets à la suite de l’identificateur du tableau.
Ainsi, si nous souhaitons par exemple définir un tableau contenant vingt int, nous devons procéder comme suit.
int tab[20];Initialisation
Comme pour les variables, il est possible d’initialiser un tableau ou, plus précisément, tout ou une partie de ses éléments. L’initialisation se réalise de la même manière que pour les structures, c’est-à-dire à l’aide d’une liste d’initialisation, séquentielle ou sélective.
Initialisation séquentielle
Initialisation avec une longueur explicite
L’initialisation séquentielle permet de spécifier une valeur pour un ou plusieurs membres du tableau en partant du premier élément. Ainsi, l’exemple ci-dessous initialise les trois membres du tableau avec les valeurs 1, 2 et 3.
int tab[3] = { 1, 2, 3 };Initialisation avec une longueur implicite
Lorsque vous initialisez un tableau, il vous est permis d’omettre la longueur de celui-ci, car le compilateur sera capable d’en déterminer la taille en comptant le nombre d’éléments présents dans la liste d’initialisation. Ainsi, l’exemple ci-dessous est correct et définit un tableau de trois int valant respectivement 1, 2 et 3.
int tab[] = { 1, 2, 3 };Initialisation sélective
Initialisation avec une longueur explicite
Comme pour les structures, il est par ailleurs possible de désigner spécifiquement les éléments du tableau que vous souhaitez initialiser. Cette initialisation sélective est réalisée à l’aide du numéro du ou des éléments.
Faites attention ! Les éléments sont numérotés à partir de zéro. Nous y viendrons dans la section suivante.
L’exemple ci-dessous définit un tableau de trois int et initialise le troisième élément.
int tab[3] = { [2] = 3 };Initialisation avec une longueur implicite
Dans le cas où la longueur du tableau n’est pas précisée, le compilateur déduira la taille du tableau du plus grand indice utilisé lors de l’initialisation sélective. Partant, le code ci-dessous crée un tableau de cent int.
int tab[] = { [0] = 42, [1] = 64, [99] = 100 };Comme pour les structures, dans le cas où vous ne fournissez pas un nombre suffisant de valeurs, les éléments oubliés seront initialisés à zéro ou, s’il s’agit de pointeurs, seront des pointeurs nuls.
Également, il est possible de mélanger initialisations séquentielles et sélectives. Dans un tel cas, l’initialisation séquentielle reprend au dernier élément désigné par une initialisation sélective. Dès lors, le code ci-dessous définit un tableau de dix int et initialise le neuvième élément à 9 et le dixième à 10.
int tab[] = { [8] = 9, 10 };Pour un tableau de structures, la liste d’initialisation comportera elle-même une liste d’initialisation pour chaque structure composant le tableau, par exemple comme ceci.
struct temps tab[2] = { { 12, 45, 50.6401 }, { 13, 30, 35.480 } } ;Notez par ailleurs que, inversement, une structure peut comporter des tableaux comme membres.
Accès aux éléments d’un tableau
L’accès aux éléments d’un tableau se réalise à l’aide d’un indice, un nombre entier correspondant à la position de chaque élément dans le tableau (premier, deuxième, troisième, etc). Cependant, il y a une petite subtilité : les indices commencent toujours à zéro. Ceci tient à une raison historique : les performances étant limitées à l’époque de la conception du langage C, il était impératif d’éviter des calculs inutiles, y compris lors de la compilation1.
Plus précisément, l’accès aux différents éléments d’un tableau est réalisé à l’aide de l’adresse de son premier élément, à laquelle est ajouté l’indice. En effet, étant donné que tous les éléments ont la même taille et se suivent en mémoire, leurs adresses peuvent se calculer à l’aide de l’adresse du premier élément et d’un décalage par rapport à celle-ci (l’indice, donc).
Or, si le premier indice n’est pas zéro, mais par exemple un, cela signifie que le compilateur doit soustraire une unité à chaque indice lors de la compilation pour retrouver la bonne adresse, ce qui implique des calculs supplémentaires, chose impensable quand les ressources sont limitées.
Prenons un exemple avec un tableau composé de int (ayant une taille de quatre octets) et dont le premier élément est placé à l’adresse 1008. Si vous déterminez à la main les adresses de chaque élément, vous obtiendrez ceci.
| Indice | Adresse de l’élément |
|---|---|
| 0 | 1008 (1008 + 0) |
| 1 | 1012 (1008 + 4) |
| 2 | 1016 (1008 + 8) |
| 3 | 1020 (1008 + 12) |
| 4 | 1024 (1008 + 16) |
| 5 | 1028 (1008 + 20) |
| … | … |
En fait, il est possible de reformuler ceci à l’aide d’une multiplication entre l’indice et la taille d’un int.
| Indice | Adresse de l’élément |
|---|---|
| 0 | 1008 (1008 + (0 * 4)) |
| 1 | 1012 (1008 + (1 * 4)) |
| 2 | 1016 (1008 + (2 * 4)) |
| 3 | 1020 (1008 + (3 * 4)) |
| 4 | 1024 (1008 + (4 * 4)) |
| 5 | 1028 (1008 + (5 * 4)) |
| … | … |
Nous pouvons désormais formaliser mathématiquement tout ceci en posant T la taille d’un élément du tableau, i l’indice de cet élément, et A l’adresse de début du tableau (l’adresse du premier élément, donc). L’adresse de l’élément d’indice i s’obtient en calculant A + T × i. Ceci étant posé, voyons à présent comment mettre tout cela en œuvre en C.
Le premier élément
Pour commencer, nous avons besoin de l’adresse du premier élément du tableau. Celle-ci s’obtient en fait d’une manière plutôt contre-intuitive : lorsque vous utilisez une variable de type tableau dans une expression, celle-ci est convertie implicitement en un pointeur sur son premier élément. Comme vous pouvez le constater dans l’exemple qui suit, nous pouvons utiliser la variable tab comme nous l’aurions fait s’il s’agissait d’un pointeur.
#include <stdio.h> int main(void) { int tab[3] = { 1, 2, 3 }; printf("Premier élément : %d\n", *tab); return 0; } RésultatPremier élément : 1Notez toutefois qu’il n’est pas possible d’affecter une valeur à une variable de type tableau (nous y viendrons bientôt). Ainsi, le code suivant est incorrect.
int t1[3]; int t2[3]; t1 = t2; /* Incorrect */La règle de conversion implicite comprend néanmoins deux exceptions : l’opérateur & et l’opérateur sizeof.
Lorsqu’il est appliqué à une variable de type tableau, l’opérateur & produit comme résultat l’adresse du premier élément du tableau. Si vous exécutez le code ci-dessous, vous constaterez que les deux expressions donnent un résultat identique.
#include <stdio.h> int main(void) { int tab[3]; printf("%p == %p\n", (void *)tab, (void *)&tab); return 0; }Notez toutefois que si l’adresse référencée par les deux pointeurs est identique, leurs types sont différents. tab est un pointeur sur int et &tab est un pointeur sur un tableau de 3 int. Nous en parlerons lorsque nous verrons les tableaux multidimensionnels un peu plus tard dans ce chapitre.
Dans le cas où une expression de type tableau est fournie comme opérande de l’opérateur sizeof, le résultat de celui-ci sera bien la taille totale du tableau (en multiplets) et non la taille d’un pointeur.
#include <stdio.h> int main(void) { int tab[3]; int *ptr; printf("sizeof tab = %zu\n", sizeof tab); printf("sizeof ptr = %zu\n", sizeof ptr); return 0; } Résultatsizeof tab = 12 sizeof ptr = 8Cette propriété vous permet d’obtenir le nombre d’éléments d’un tableau à l’aide de l’expression suivante.
sizeof tab / sizeof tab[0]Notez que pour obtenir la taille d’un type tableau, la syntaxe est la suivante.
#include <stdio.h> int main(void) { printf("sizeof int[3] = %zu\n", sizeof(int[3])); printf("sizeof double[42] = %zu\n", sizeof(double[42])); return 0; } Résultatsizeof int[3] = 12 sizeof double[42] = 336Les autres éléments
Pour accéder aux autres éléments, il va nous falloir ajouter la position de l’élément voulu à l’adresse du premier élément et ensuite utiliser l’adresse obtenue. Toutefois, recourir à la formule présentée au-dessus ne marchera pas car, en C, les pointeurs sont typés. Dès lors, lorsque vous additionnez un nombre à un pointeur, le compilateur multiplie automatiquement ce nombre par la taille du type d’objet référencé par le pointeur. Ainsi, pour un tableau de int, l’expression tab + 1 est implicitement convertie en tab + sizeof(int).
Voici un exemple affichant la valeur de tous les éléments d’un tableau.
#include <stdio.h> int main(void) { int tab[3] = { 1, 2, 3 }; printf("Premier élément : %d\n", *tab); printf("Deuxième élément : %d\n", *(tab + 1)); printf("Troisième élément : %d\n", *(tab + 2)); return 0; } RésultatPremier élément : 1 Deuxième élément : 2 Troisième élément : 3L’expression *(tab + i) étant quelque peu lourde, il existe un opérateur plus concis pour réaliser cette opération : l’opérateur []. Celui-ci s’utilise de cette manière.
expression[indice]Ce qui est équivalent à l’expression suivante.
*(expression + indice)L’exemple suivant est donc identique au précédent.
#include <stdio.h> int main(void) { int tab[3] = { 1, 2, 3 }; printf("Premier élément : %d\n", tab[0]); printf("Deuxième élément : %d\n", tab[1]); printf("Troisième élément : %d\n", tab[2]); return 0; }Parcours et débordement
Une des erreurs les plus fréquentes en C consiste à dépasser la taille d’un tableau, ce qui est appelé un cas de débordement (overflow en anglais). En effet, si vous tentez d’accéder à un objet qui ne fait pas partie de votre tableau, vous réalisez un accès mémoire non autorisé, ce qui provoquera un comportement indéfini. Cela arrive généralement lors d’un parcours de tableau à l’aide d’une boucle.
#include <stdio.h> int main(void) { int tableau[5] = { 784, 5, 45, -12001, 8 }; int somme = 0; for (unsigned i = 0; i <= 5; ++i) somme += tableau[i]; printf("%d\n", somme); return 0; }Le code ci-dessus est volontairement erroné et tente d’accéder à un élément qui se situe au-delà du tableau. Ceci provient de l’utilisation de l’opérateur <= à la place de l’opérateur < ce qui entraîne un tour de boucle avec i qui est égal à 5, alors que le dernier indice du tableau doit être quatre.
N’oubliez pas : les indices d’un tableau commencent toujours à zéro. En conséquence, les indices valides d’un tableau de n éléments vont de 0 à n - 1.
Tableaux et fonctions
Passage en argument
Étant donné qu’un tableau peut être utilisé comme un pointeur sur son premier élément, lorsque vous passez un tableau en argument d’une fonction, celle-ci reçoit un pointeur vers le premier élément du tableau. Le plus souvent, il vous sera nécessaire de passer également la taille du tableau afin de pouvoir le parcourir.
Le code suivant utilise une fonction pour parcourir un tableau d’entiers et afficher la valeur de chacun de ses éléments.
#include <stdio.h> void affiche_tableau(int *tab, unsigned taille) { for (unsigned i = 0; i < taille; ++i) printf("tab[%u] = %d\n", i, tab[i]); } int main(void) { int tab[5] = { 2, 45, 67, 89, 123 }; affiche_tableau(tab, 5); return 0; } Résultattab[0] = 2 tab[1] = 45 tab[2] = 67 tab[3] = 89 tab[4] = 123Notez qu’il existe une syntaxe alternative pour déclarer un paramètre de type tableau héritée du langage B (voyez la section suivante).
void affiche_tableau(int tab[], unsigned taille)Toutefois, nous vous conseillons de recourir à la première écriture, cette dernière étant plus explicite.
Retour de fonction
De la même manière que pour le passage en argument, retourner un tableau revient à retourner un pointeur sur le premier élément de celui-ci. Toutefois, n’oubliez pas les problématiques de classe de stockage ! Si vous retournez un tableau de classe de stockage automatique, vous fournissez à la fonction appelante un pointeur vers un objet qui n’existe plus (puisque l’exécution de la fonction appelée est terminée).
#include <stdio.h> int *tableau(void) { int tab[5] = { 1, 2, 3, 4, 5 }; return tab; } int main(void) { int *p = tableau(); /* Incorrect */ printf("%d\n", p[0]); return 0; }- Pour les curieux, vous pouvez lire ce billet (en anglais) qui explique en détail les raisons de ce choix.↩
Tag » Code C Copier Tableau
-
Copier Le Tableau De Caractères En C | Delft Stack
-
Copie D'un Tableau En C Par RaidenS - Page 1 - OpenClassrooms
-
[C] Copier Un Tableau [Résolu] - CCM
-
11.5. Copie D'un Tableau : For Ou Memmove
-
Copier Les Valeurs Positifs D'un Tableau - Langage C - Exelib
-
Copier Une Chaîne De Caractère En C - WayToLearnX
-
Programme C Pour Copier Un Tableau Dans Un Autre
-
Copie De Tableaux De Structures En C - AskCodez
-
Créer Une Copie D'un Array En C++ - Techie Delight
-
Copier Un Tableau à 2 Dimensions (matrice) En C++ - Techie Delight
-
Langage C - Les Tableaux
-
X. Tableaux Et Chaînes De Caractères - Le C En 20 Heures
-
Les Pointeurs : Copié Un Tableau B à La Fin D'un Tableau A - YouTube
-
Programmation C/Tableaux - Wikilivres