Programmation C source/tableaux
/* inclut stdio.h qui décrit
 - la fonction printf */
/* fonction main, 
 point d'entrée du programme,
 executée pour démarrer le programme par le système d'exploitation */
int main(void)
 printf("--------------------\n");
 printf(" Déclaration\n");
 printf("--------------------\n");
 int tableau1[10];
 // déclaration d'un tableau de 10 entiers
 int a = 3;
 int tableau2[a + 2];
 /* déclaration d'un tableau à longueur variable (ici après calcul de 5 entiers)
 valide en C norme C99
 ne peut pas être static ou extern
 sa portée est limitée au bloc dans lequel il est défini
 (c'est à dire il n'existe que dans le bloc ou il est défini)
 les risques de fuite mémoire sont supprimés
 (on a pas besoin de libérer la mémoire) */
 /* static
 pour un objet local à une fonction :
 la valeur de la variable sera persistante entre les différents appels de la
 fonction. La variable ne sera visible que dans la fonction, mais ne sera pas
 réinitialisée à chaque appel de la fonction.
 pour un objet global et une fonction :
 comme une variable globale est déjà persistante, le mot-clé static aura pour
 effet de limiter la visibilité de la variable ou de la fonction au seul fichier
 où elle est déclarée
 /* extern
 extern permet de déclarer une variable sans la définir. C'est utile pour la
 compilation séparée, pour définir une variable ou une fonction dans un fichier,
 en permettant à des fonctions contenues dans d'autres fichiers d'y accéder.
 Toutes les variables globales et fonctions qui ne sont pas déclarées (ou
 définies) static sont externes par défaut.
 printf("\n--------------------\n");
 printf(" Accès aux éléments\n");
 printf("--------------------\n");
 int tableau3[4];
 tableau3[0] = 1;
 tableau3[1] = 2;
 tableau3[2] = 3;
 tableau3[3] = 4;
 3[tableau3] = 4;
 // fonctionne aussi mais à éviter
 int i;
 for(i = 0; i < 4; i++)
 tableau3[i] = 0;
 // tableau3 est mis à 0
 printf("Erreur accès à un élément hors limites du tableau,\ntableau3[6] = %d\n",
 tableau3[6]);
 // attention ! aucun garde fou, peut faire planter le programme
 printf("\n--------------------\n");
 printf(" Tableaux à\n");
 printf(" plusieurs\n");
 printf(" dimensions\n");
 printf("--------------------\n");
 int matrice[2][5];
 matrice[0][0] = 1;
 matrice[0][1] = 2;
 matrice[0][2] = 3;
 matrice[0][3] = 4;
 matrice[0][4] = 5;
 matrice[1][0] = 1;
 matrice[1][1] = 2;
 matrice[1][2] = 3;
 matrice[1][3] = 4;
 matrice[1][4] = 5;
 int b = 3;
 int matrice2[b + 3][2 * 2];
 /* déclare une matrice à longueur variable (ici après calcul [6][4])
 valide en C norme C99
 ne peut pas être static ou extern
 sa portée est limitée au bloc dans lequel il est défini */
 printf("\n--------------------\n");
 printf(" Initialisation\n");
 printf(" des tableaux\n");
 printf("--------------------\n");
 int tableau4[5] = { 0 , 1 , 2 , 3 , 4 };
 int tableau5[] = { 0 , 1 , 2 , 3 , 4 };
 // le compilateur calculera la taille de tableau5 soit 5
 int tableau6[3] = { 0 };
 /* Si on déclare un tableau de taille fixe, mais qu'on l'initialise avec moins
 d'éléments qu'il peut contenir, les éléments restant seront mis à zéro
 donc remplacé par int tableau[3] = { 0, 0, 0 };
 Cette technique est néanmoins à éviter, car dans ce cas toutes les valeurs du
 tableau seront stockés en intégralité dans le fichier exécutable du programme,
 faisant grossir inutilement sa taille.
 il vaut mieux utiliser une boucle
 exemple : */
 int tableau7[500];
 int j;
 for(j = 0; j < 500; j++)
 tableau7[j] = 0;
 int matrice3[2][3] = { { 0, 1, 2 }, { 0, 1, 2 } };
 int matrice_identite1[][3] = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };
 /* le compilateur calculera la taille de matrice_identite1[][3] soit [3][3]
 on peut ne pas préciser la première dimension
 et encore plus simplement on peut écrire : */
 int matrice_identite2[][3] = { { 1 }, { 0, 1 }, { 0, 0, 1 } };
 printf("\n--------------------\n");
 printf(" Conversion des\n");
 printf(" noms de tableaux\n");
 printf(" en pointeurs\n");
 printf("--------------------\n");
 int tab[10];
 int * p;
 p = tab;
 /* un nom de tableau apparaissant dans une expression C
 sera automatiquement converti
 en un pointeur vers son premier élément */
 printf("p = %p\n", p);
 p = &tab[0];
 printf("p = %p\n", p);
 /* Cas où tab n'est PAS converti en pointeur vers son premier élément
 c'est à dire conserve son type "tableau d'entiers à 10 éléments"
 1) int tab[10]; tab est de type "tableau d'entiers à 10 éléments"
 2) &tab int tab[10]; tableau d'entiers à 10 éléments
 int (* p1)[10]; pointeur vers les tableaux d'entiers à 10 éléments
 attention au parenthèses
 int * p3[10]; équivaut à int * (p3[10])
 p1 = &tab; ceci est juste !
 int * p2;
 p2 = &tab; incorrect ! &tab et p2 ne sont pas du même type
 3) sizeof tab = 10 * (la taille d'un int en nombre de char)
 si un int vaut 32 bits et un char 8 bits
 10 * (32 / 8) = 10 * 4 = 40
 4) tab++, ++tab, tab-- ou --tab erreur !
 tab est de type "tableau d'entiers à 10 éléments"
 impossible d'effectuer ++ ou --
 5) tab = expr dans ce cas, tab est de type "tableau d'entiers à 10 éléments"
 prenons expr une expression quelconque
 on se trouve dans un cas impossible !
 car expr ne sera jamais du type "tableau d'entiers à 10 éléments"
 preuve par l'exemple : tab1 = tab2
 tab2 lui est converti en un pointeur vers son premier élément 
 6) tab.champ tab est de type "tableau d'entiers à 10 éléments"
 impossible!
 tab ne possède aucun champ */
 /* sinon tab est toujours converti en pointeur vers son premier élément
 autre exemple, comparaison : tab_1 == tab_2
 tab1 et tab2 sont convertis chacun en pointeur vers leur premier élément
 et la comparaison effectuée sera celle des adresses. */
 printf("\n--------------------\n");
 printf(" Cas des paramètres\n");
 printf(" de fonctions\n");
 printf("--------------------\n");
 void fonction1(int tableau8[10]) 
 // ...
 void fonction2(int tableau8[]) 
 // ...
 void fonction3(int * ptr_sur_tableau8) 
 // ...
 /* fonction3 équivaut fonction2 et fonction1
 tout paramètre de fonction déclaré comme étant de type "tableau d'élements"
 est automatiquement converti en un pointeur à la compilation
 il est recommandé d'utiliser l'écriture avec pointeur, voir fonction3 */
 int tableau9[10];
 fonction3(tableau9);
 // fonction3(tableau9) tableau9 est converti en pointeur vers son premier élément
 void fonction4(int matrice4[10][10])
 // ...
 void fonction5(int matrice5[][10])
 // ...
 void fonction6(int (* ptr_sur_matrice6)[10])
 // ...
 /* fonction6 équivaut fonction5 et fonction4
 il est recommandé d'utiliser l'écriture avec pointeur, voir fonction6 */
 printf("\n--------------------\n");
 printf(" Cas des\n");
 printf(" tableaux externes\n");
 printf("--------------------\n");
 /* fichier1.c
 int tableau10[5];
 fichier2.c
 extern int * tableau10; <-- FAUX !
 extern int tableau10[]; <-- JUSTE */
 printf("\n--------------------\n");
 printf(" Tableaux VLA passés\n");
 printf(" à une fonction\n");
 printf("--------------------\n");
 // VLA (Variable Length Array) tableau à longueur variable
 int c = 3, d = 4;
 int matrice7[c][d];
 int k = 0, l = 0, m = 1;
 for(k = 0; k < c; k++)
 for(l = 0; l < d; l++)
 matrice7[k][l] = m;
 m++;
 // initialisation de matrice7
 void afficheVlaMatrice(int taille1, int taille2, int (* matrice)[taille2])
 int i, j;
 for (i = 0; i < taille1; i++)
 for (j = 0; j < taille2; j++)
 printf("matrice[%d][%d] = %d\n", i, j, matrice[i][j]);
 /* En C99, il est possible de passer à une fonction un tableau
 de type VLA (Variable Length Array).
 Il faut passer les tailles des dimensions en premier. */
 afficheVlaMatrice(c, d, matrice7);
 printf("\n--------------------\n");
 printf(" Chaînes\n");
 printf(" de caractères\n");
 printf("--------------------\n");
 /* lorsque j'écris chaine1 = "hello"
 j'écris en fait char temp[] = { 'h', 'e', 'l', 'l', 'o', 0 };
 chaine1 = temp;
 temp est converti en pointeur sur son premier élément
 donc chaine1 est de type char *
 de plus en C on ne pourra pas modifier "hello" */
 char * chaine1 = "hello";
 /* Dans ce premier cas on déclare
 un pointeur sur une chaîne de caractères statique
 (les données sont persistantes entre les différents appels de fonctions
 et sur certaines architectures, pour ne pas dire toutes,
 elle est même en lecture seule) */
 char chaine2[] = "hello";
 /* est un raccourci pour char chaine2[] = { 'h', 'e', 'l', 'l', 'o', 0 };
 Dans ce second cas, on déclare un tableau
 (alloué soit sur la pile si la variable est déclarée dans une fonction
 ou soit dans le segment global si la variable est globale)
 de taille suffisante pour contenir
 tous les caractères de la chaîne affectée (incluant le caractère nul).
 Ce second cas est donc une notation abrégée */
 chaine2[0] = 'a';
 chaine2[1] = 'b';
 // ... possible avec chaine2[] uniquement
 /* Dans tous les autres cas (ceux où une chaîne de caractères ne sert pas
 à initialiser un tableau), la chaîne déclarée est statique */
 /* Le compilateur peut optimiser la gestion des chaînes
 en regroupant celles qui sont identiques. 
 C'est pourquoi il est préférable de classifier
 les pointeurs sur chaîne de caractères avec le mot clé const. */
 const char * chaine3 = "Ceci est une chaine";
 return 0;