Programmation C source/tableaux
Apparence
#include <stdio.h>
/* 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;
}