Tableaux en C
Les tableaux permettent de placer des variables strictement concommitantes les unes aux autres en mémoire.
En effet lorsque l’on déclare des variables sur la pile, bien qu’elles soient empillées les un es après les autres il n’y a pas de garantie vis à vis de leur agencement en mémoire, ou si il y a du vide entre elles etc.
int main()
{
char toto;
int tata;
}
Même si l’on dit que ces vriables sont déclarés sur la pile (stack) rien ne spécifie vers quel sens se propage la pile et si le compilateur ajoute ou non de l’espacement entre celles-ci. Le compilateur microsoft par exemple avec les drapeaux de compilation de type “Debug” ajoute des zones mémoires empoisonnées pour vérifier les lecture ou écrites hors champ, cela permet de détecter de nombreuses erreurs de programmation (bugs).
Un tableau permet de garantir que plusieurs valeurs seront immédiatement concommitantes
les unes aux autres sans aucun espacement, la seule restriction est que
toutes les variables soient du même type. Voici comment on déclare
un tableau de 20 int
int main()
{
int tab[20]; // Alloue suffisment d'espace sur la pile pour contenir 20 int non initialisés
char tab2[4] = {'t', 'o', 't', 'o'}; // Alloue un tableau de 4 char et l'initialise
char tab3[4] = "tata"; // Le C autorise d'utiliser une chaine de caracteres
// pour initialiser un tableau de char ainsi, le charactère nul terminal
// est silencieusement tronqué. Cela n'est pas le cas en C++
}
On accède au n-ieme élément avec la même écriture en crochets tab[n-1]. L’élément
à l’indice zero est le premier élément, le deuxième élement est à l’indice 1, etc.
Les tableaux en C commencent à zero donc il faut décaler nos indices de 1
vers la gauche.
L’élément entre crochets dit quelle distance
doit être parcourue à partir de l’adresse de base du tableau, car derrière la syntaxe
tab[3], ce qui est réllement exécuté est *(tab + 3). Lire l’élément de la première
case consiste à écrire *(tab + 0), qui se simplifie en *tab.
#include <stdio.h>
int main()
{
int tab[20] = { 1 }; // initialise la première case à 1, le reste à zero
char tab2[1]= {'t', 'o', 't', 'o'};
printf("%d, %d, %c, %c\n", tab[0], tab[10], tab2[0], tab2[3]);
// affiche "1, 0, t, o"
}
Arithmétique de pointeurs
Le fait d’avoir des valeurs concomitantes en mémoire permet d’appliquer des opérateurs arithmétiques sur des pointeurs qui pointent sur ces régions de la mémoire en relative sécurité.
Si on a pointeur p qui pointe sur la troisième case d’un tableau,
alors p + 1 est un pointeur qui pointe sur la quatrième case de ce tableau
et p - 1 est un pointeur qui pointe sur la deuxième case de ce tableau.
On peut donc avoit un pointeur et lui additionner ou soustraire un entier , dans ce cas le résultat est un entier décalé d’autants d’octets que le type pointé, multiplié par la valeur de l’entier.
T * ptr;
ptr + n; // equivaut a (ptr + sizeof T * n)
une arithmétique des pointeurs. En effet, lorsqu’on possède un pointeur sur une case d’un tableau, on peut accéder à n’importe quelle case de ce tableau
Troncature chaines
Depuis le C Ansi , il est possible en donnant une taille explicite à un tableau de l’initialiser avec une chaîne de caractères littérale de la même taille que le tableau, dans ce cas le caractère nul terminal est silencieusement tronqué.
#include <stdio.h>
int main()
{
char toto[4] = "toto";
// valide depuis le C Ansi, invalide en C++
printf("%zu %zu\n", sizeof toto, sizeof "tata");
// affiche 4 et 5
}