Aller au contenu

Programmation C source/tableaux

Un livre de Wikilivres.
Programmation C source
Programmation C++
Programmation C++
Sommaire
Modifier ce modèle
#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;
}