Chapitre 7
LES TABLEAUX.

7.1. Tableaux monodimensionnels.

Un tableau  est une collection de données de même type.

Pour définir une variable tableau tab de n>0 éléments de type T_Type, on écrit:

T_type tab[n];

Exemples

int t[3];  //  variable de type tableau composé de 3 entiers:
Composantes : t[0] , t[1] et t[2].

float longueurs[50];
longueur[0] est la première composante,
longueur[49] en est la dernière.

Remarque
Certains compilateurs exigent que la taille d'un tableau soit une constante, mais d'autres, les meilleurs,  permettent que n soit une variable dont la valeur est déterminée au moment de l’exécution.

Exercice.
Écrire un programme qui permet la saisie d’au plus 20 nombres réels dans un tableau, puis calcule et affiche la moyenne des données contenues dans ce tableau.

// Exemple de calcul de la moyenne d'un tableau
#include <stdio.h>
void main()
	{
    #define NombreElements 20 //taille maximun du tableau
	
	float t[NombreElements], // définition du tableau t
	somme=0.0; // pour la somme cumulée
	int i; // index 
	
	for(i=0;i<NombreElements;i++)	
	  { 
		t[i]=0;// mise à zero avant saisie
	    	printf("Saisir t[%i] = ",i);
       		scanf("%f",&t[i]);

		if (feof(stdin)) break; // a la suite de ^Z
		}
 
// Cumul des valeurs de t
for ( i=0 ; ((t[i]!=0) && (i<NombreElements)); somme+=t[i++]);

//========  Affichage de la moyenne	====
puts("\n");
if (i>0) printf("La moyenne sur %i elements est %f\n",i,somme/i);
else puts("tableau vide.");
}

7.2. Initialisation des tableaux.

L'initialisation d'un tableau peut être réalisée à l'aide d'une paire d'accolades qui regroupe les valeurs.

Exemple
char t[10]={'a','b'};
t[0] reçoit 'a' et t[1] recoit b, toutes les autres composantes sont mises à '\0'

On ne peut pas sous dimensionner un tableau.
int x[2]={2,1,5,6}; // ERREUR

Exercice
Écrire un programme qui permet la saisie au clavier, d’au plus 10 caractères. La fin de saisie se fait avec le caratère '.' Avant d’être mémorisé dans un tableau, chaque caractère est transformé en son successeur. Le programme se termine en affichant tableau ainsi transformé.

// Exemple de calcul de la moyenne d'un tableau
#include <stdio.h>
void main() {
#define DIM 10 // Taille maximale du tableau
int i=0; // index
unsigned char c='\0',
t[DIM]={'\0'}; // tous les éléments sont mis à zéro
puts("Entrer une suite de caracteres fini par un . : ");

do {
	scanf("%c",&c);
	if (c=='.') break; // fin de saisie
	t[i++]=c+1;
  } while  (i<DIM) ;

// affichage du résultat
for(i=0;   (t[i]!='\0') && (i<DIM) ; printf("%c",t[i++]));
puts("\n");

}

7.3. Limites des tableaux.

Tout tableau possède une longueur bien définie :
	
0 est l'indice minimal

L'indice maximal vaut la taille du tableau moins un. 

ATTENTION
Peu de compilateurs vérifient la limite des indices des tableaux

7.4. Tableaux et paramètres de fonctions

Règle
Un tableau est toujours transmis par référence (adresse) . Il faut transmettre aussi sa dimension .

Exercice
écrire une fonction de prototype
 void Afficher(const int tab[], const int dimension); permettant d’afficher le contenu d’un tableau tab dont on connait la dimension.
Tester cette fonction en utlisant la macro fonction
#define PrintTabInt(t) Afficher(t,sizeof(t)/sizeof(int))

#include <stdio.h>
#define PrintTabInt(t) Afficher(t,sizeof(t)/sizeof(int))

// Macro fonction affichant un tableau d'entiers

void Afficher(const int tab[], const int dimension)
{ int i;
   for( i=0;i<dimension;i++)
  printf("%i ",tab[i]);  }

  //                           Programme test
 void main() {
 int x[]={4,9,12,23,5,11,9};
// la dimension est ici calculée par le comilateur
 PrintTabInt(x); 
 }

7.5. Exemple d'application : le tri d'un tableau.

Le tri dans un tableau de données est une opération importante dans beaucoup d'applications.

Principe du trie bulle (bubble sort) sur un tableau d'entiers.
Le tri à bulle consiste à itérer en examinant le tableau jusqu'à ce qu'il soit trié, en une série de passages successives. Durant chaque passage, une seconde boucle parcours le tableau à la recherche de paire d'éléments qui peuvent être échangés afin d'être mis dans le bon ordre. A chaque passage quelques éléments supplémentaires ont été triés.

//                                Trie à bulles
int compare(const int, const int);
 // comparaison des données à trier

void echange(int*, int *);
// fonction d'échange

//                                     Procedure de tri
void trier(int  table[], const int taille) {
/* booléen permettant de sortir immédiatement si le tableau est
déjà  trié au milieu d'une boucle */
int EstTrie=0;

int i,j; //index
// On itère jusqu'à ce que le tableau soit trié
for ( i= 0 ; (i<taille-1) && ! EstTrie; i++)
{  EstTrie=1;  // on suppose que le tableau est trié

/*En partant d'une extrémité du tableau, comparaison des paires
 d'éléments et tri local.
 Après chaque boucle i, un élément supplémentaire au moins sera
à sa position finale. On optimise en restreignant
le nombre d'itérations.*/  

 for (j=0; j<taille-1;j++)   {
  if (! compare(table[j],table[j+1])) 
{ EstTrie= 0 ;
 // La table n'était pas triée.
  
 echange(&table[j],&table[j+1]);    }    }   }    }

  //                    Le prédicat compare
  int compare(const int x, const int y)
  {  return x<y ;   }

   //             La procédure echange
  void echange(int * x, int * y)
  {
    int temp;
    temp=*x;
    *x=*y;
    *y=temp;
  }
//===================================================
  //                Programme Testant la fonction de trie
 #include <stdio.h>
void Afficher(const int tab[],const int taille)   {
int i;
for (i=0; i<taille; i++) printf("%i ",tab[i]); 
return;  }

//                      Saisie des données  
void Get(int t[],int *dim,const int max)
  { for(*dim=0; (!feof(stdin) ) ;(*dim)++ ) {
if (*dim<max) {
	printf("\nt[%i]=",*dim);
    scanf("%i",&t[*dim]);
	}
else  return; 	}
       --(*dim);  }

//                                    Programme Principal 
//------------------------------------------------------------------------------
void main() {
#define TAILLEMAX 5

 int tab[TAILLEMAX],dimension=0;
puts("\n");
 Get(tab,&dimension,TAILLEMAX);
 trier(tab,dimension);
 Afficher(tab,dimension); }

7.6. Chaînes et tableaux de caractères.

Une chaîne de caractères en C++ est un tableau de caractères qui se termine par le caractère '\0'.

7.6.1. Les littéraux.

Sur une même ligne, toute suite de caractères incluse entre deux " constitue une chaîne de caractères valide.

Un littéral de type chaîne peut être réparti sur plusieurs lignes de deux manières :

2. Lorsque le dernier caractère d'une ligne est \ , le compilateur ignore celui-ci ainsi que le retour chariot suivant.

1. Deux littéraux consécutifs sont concaténés en une seule chaine :

#include <iostream.h>
 void main() {
 cout<<"Bonjour" "
           "cher ami "
           "a bientot" <<endl;
  }

#include <stdio.h>
void main() {
puts( "Bonjour \
cher ami, \
comment allez vous ?");
}

Le compilateur ajoute automatiquement le '\0' de fin
 de chaîne à toute constante chaîne de caractères.

ATTENTION
Le caractère de fin de chaîne  '\0' ne fait pas partie de la chaîne de caractères, mais fait partie du tableau de caractères qui contient cette chaîne.

7.6.2. Initialisations.

Exemple d'initialisations de variables chaînes.

char mot1[]={ 'B','o','j','o','u','r','s','\0'};
 char mot2[]="Salut";
 void main() {
 char mot3[]={'M','I','A','S','\0'};
 char mot4[]="INFORMATIQUE";
puts(mot1);
 puts(mot2);
 puts(mot3);
 puts(mot4);
 }

7.6.3. Chaînes et entrées/sorties en C.

1.	Soit s une variable chaîne de caractères.
A l'appel  printf (s) affichera chaque caractère composant s, jusqu'à ce quelle trouve le '\0' final. Il en est de même pour puts(s).

2.	La lecture d'une chaîne par scanf est plus complexe.
Problèmes liés à la taille fixe des tableaux.
Le programme de lecture est lié à la prévision de la taille de la chaîne. Il est nécessaire que la taille de la variable chaîne soit suffisante pour contenir tous les caractères lus par scanf.
S'assurer que le tampon sera assez grand pour accepter la plus longue chaîne entrée par l'utilisateur. Mais ceci est difficilement prévisible !
Cette lecture se termine par un caractère séparateur : retour chariot, caractère espace, tabulation. La fonction scanf substitue le caractère séparateur par le caractèr nul '\0', terminant ainsi la saisie.
Exemple :

#include <stdio.h>
void main()
{ 
#define TailleBuffer 20
 // Taille maximale  prévue y compris le '\0'

char Buffer[TailleBuffer]; 
// pour lire les caractères
puts("Tapez une chaine :");
scanf("%s",Buffer);
printf( "La chaine est %s :",Buffer);
  }

Buffer stocke les caractères entrés par l'utilisateur.

Compléments. Saisie contrôlée d'une chaîne de caractères.

Une solution possible est d'utiliser la fonction fgets , qui permet de résoudre débordement du tampon.
Cette fonction fgets peut utiliser le fichier d'entré standard stdin (qui par défaut correspond au clavier). 
fgets(s,n,sdin);
permet de saisir, à partir du clavier, dans une chaîne de caractères s, n caractères, y compris le '\0' final. La saisie se termine si n-1 caractères, ou le retour chariot, ont été tapés au clavier. La fonction fgets permet donc de saisir tous les caractères, autre que le CR (retour chariot). Le CR est substitué avec le caractère NULL '\0'.(Lecture d'une ligne.)

void main()
{ #define TailleBuffer 20 
//Taille maximale  prévue y compris le '\0'
char  Buffer[TailleBuffer]; 
// pour lire les caractères
puts("Tapez une chaine :");
fgets(Buffer,4,stdin);
/* 3 caractères seront saisis, le '\0' sera ajouté
par la fonction fgets */
printf( "La chaine est %s :",Buffer); }

7.6.4. Primitives sur la gestion des chaine de caractères.

Voir le chapitre 8 concernant la gestion des pointeurs.

7.7. Tableaux à plusieurs dimensions.

Une variable tableau x à deux dimensions ayant n “ lignes ” et m “ colonnes ” d'un type T_Type est définie par :

T_Type  x[n][m] ;

Exemples
		char tab2[20][5];
		float x[2][3];

	

La différence principale entre un tableau à deux dimensions et un tableau à une dimension vient de l'initialisation. 

Exemple
		int t[2][3] = {  {1,2,3}, {4,5,6} };
Ce tableau est composé de deux "lignes" et de trois "colonnes".
Les valeurs sont rangées linéairement dans l'ordre suivant :
	t[0] [0]	t[0] [1]         t[0] [2]                   // ligne 0
	t[1] [0]	t[1] [1]	   t[1] [2]                   // Ligne 1     

et l'initialisation est faite ligne par ligne :
 	t[0][]={1,2,3}    // ligne 0 : t[0][0]=1	t[0][1]=2	 t[0][2]=3

	t[1][]={3,4,5}   // ligne  1 : t[1][0]=4	t[0][1]=5	 t[1][2]=6

int q[3][2]={ {1},{1}}

	q[0][]={1}	//ligne 0  : q[0][0]=1	 q[0][1]=0
	q[1][]={1}	//ligne 1  : q[1][0]=1	 q[1][1]=0	 
	q[2][]={0}	//ligne 2 :  q[2][0]=1   q[2][1]=0
	
Remarque
Les tableaux à une dimension à coefficients réels correspondent  au concept mathématique de vecteur.

Les tableaux à deux dimensions à coefficients réels correspondent   au concept mathématique de matrice rectangulaire.

On peut généraliser les tableaux à dimensions > 2, qui eux sont rarement utilisés.

T_Type t[n1][n2][n3]...[nr] ;

7.8. Exercices de révision.

7.8.1. Ecrire un programme qui analyse un texte.

Après avoir lu un texte au clavier, le programme doit afficher le nombre de lignes, de voyelles, de consonnes, de mots. Ce programme doit aussi afficher le nombre de mots ligne par ligne, le nombre total de mots composant le texte, et pour chaque caractère présent dans le texte le nombre de fois où il apparaît dans celui ci.

Exemple de test de ce programme.

****************
L'homme n'est ni ange ni bête, et le
malheur veut que qui fait l'ange fait
la bête. PASCAL.
****************
Nombre de lignes : 3
Nombre de voyelles : 29
Nombre de consonnes : 37
Ligne1 : 10 mots
Ligne2 : 8 mots
Ligne3 : 3 mots
Nombre total de mots : 21
[
]=3 [ ]=31 [']=3 [,]=1 [.]=2 [A]=2 [C]=1
[L]=2 [P]=1 [S]=1 [a]=6 [b]=2 [e]=11 [f]=2
[g]=2 [h]=2 [i]=5 [l]=4 [m]=3 [n]=5 [o]=1
[q]=2 [r]=1 [s]=1 [t]=7 [u]=4 [v]=1 

//Solution.
// Programme d'analyse de texte.

#include <stdio.h>
#include <ctype.h>

#define TAILLE_LIGNE 80 
// maximun de caracteres par lignes 

#define TAILLE_TEXTE 25 
//maximun de lignes

#define NB_ASCII 256

typedef char Texte[TAILLE_TEXTE][TAILLE_LIGNE] ;
//le texte lui-meme

typedef char occurence[NB_ASCII];
// occurence[i] = nb d'occurence du caractere de code ASCII i

typedef unsigned NbVoyelles;

typedef unsigned NbConsonnes;

typedef unsigned Mot_Par_Ligne[TAILLE_TEXTE];
//nb occurence de mots par ligne

typedef unsigned NombreTotaldeMots;
//pour le texte

void lire(Texte);
void ecrire(const Texte);
unsigned nb_mot(const char[]);

// nombre de mots sur une ligne donne
unsigned stat_car(const Texte,
		  occurence,
		  Mot_Par_Ligne,
		  NombreTotaldeMots * ,
		  NbVoyelles  * ,
		  NbConsonnes *);
// retourne le nombre de lignes d'un texte
//occurence : fréquence de chaque caractere
//Mot_Par_Ligne : fréquence des mots par lignes
//NombreTotaldeMots : retourne le nombre total de mots
// NombreVoyelles : du texte
//NombreConsonnes : du texte
int separateur(const char);
 //retourne le nombre de lignes,d'occurences de caractéres et de mots)
//*************************************
void lire(Texte t){
register unsigned  ligne=0;  //courante
     while (!feof(stdin)) {   // prise ligne par ligne
		    if (ligne>TAILLE_TEXTE-1) return;
		    fgets(t[ligne++],TAILLE_LIGNE-1,stdin);
		     }
		      }

//*****************************
void ecrire(const Texte t){
	register  unsigned ligne;
  for (ligne=0; t[ligne][0];ligne++)
   puts(t[ligne]);
}
//-----------------------------
unsigned nb_mot(const char t[]){
register short mot_nouveau=1;
unsigned i, nb=0;
//ignorer les espaces
for(i=0; t[i] ; i++)
{ if (separateur(t[i])) { mot_nouveau=1;continue;}
  //sinon
   if (mot_nouveau) { mot_nouveau=0; ++nb;}
 }
 return nb;
 }
//*********************************************
int est_voyelle(const char c) {
char car;
 // c'est lettre minuscule ou majuscule
  if (!isalpha(c)) return 0;
  car=tolower(c); //Conversion en minuscules
return
  (
    (isalpha(c))
    &&(     (car=='e')
	 || (car=='a')
	 || (car=='i')
	 || (car=='o')
	 || (car=='u')
	 || (car=='y')
     ) 
 ) ? 1:0 ; 
}

int est_consonne(const char c) {
char car=tolower(c); //Conversion en minuscules
return
  (
    (isalpha(c))
    &&(! ( (car=='e')
	 || (car=='a')
	 || (car=='i')
	 || (car=='o')
	 || (car=='u')
	 || (car=='y')
     ) )
 ) ? 1:0 ;
			       }

int separateur(const char c) {
return
      (isspace(c)||(c=='\'')
		 ||(c==',')
		 ||(c==';')
		 ||(c==':')
		 ||(c=='.')
		 ||(c=='-')
		 ||(c==')')
		 ||(c=='(')
		 ||(c=='[')
		 ||(c==']')
		 );   }

//========================================
unsigned stat_car(const Texte t,
		  occurence s,
		  Mot_Par_Ligne nb ,
		  NombreTotaldeMots * NbTotalMots,
		   NbVoyelles * nv,
		  NbConsonnes * nc)
		   {
 //*nb doit etre déja mis à z‚ro
unsigned ligne;
int i;
  *NbTotalMots=*nc=*nv=0;

 for( ligne=0 ; t[ligne][0];
			     nb[ligne]+=nb_mot(t[ligne]),
			      (*NbTotalMots)+=nb[ligne],
			     ligne++)

    for ( i=0; (t[ligne][i]) ;i++) {
				++s[ t[ligne][i] ];
				(*nc)+=est_consonne(t[ligne][i]);
				(*nv)+=est_voyelle(t[ligne][i]);
				      }
   return ligne;   }
//**************************************
void afficher_nb_occurences( occurence s) {
short i,nb_lettres;
for (  i=0, nb_lettres=0; i<NB_ASCII ; i++) {
    if ( s[i] )
	{ 	printf("[%c]=%i ", i ,s[i]);
        ++nb_lettres;
      if (nb_lettres %7==0) puts("");
       }           
   }
 }

//===============================================
void afficher_nb_mots_par_ligne(Mot_Par_Ligne nbm)
{  int i;
	for(i=0; nbm[i] ; i++)
	printf("\nLigne%i : %i mots\n",i+1,nbm[i]);
}
//===================
void main() {
occurence s={0};
Mot_Par_Ligne nbMotsParLigne={0};
NombreTotaldeMots NbMots;
NbVoyelles nbv;
NbConsonnes nbc;
Texte x={'\0'};

puts("Entre le texte :");
lire(x);
puts("****************");
ecrire(x);
puts("****************");

printf("\nNombre de lignes : %i", stat_car(x,
					   s,
					   nbMotsParLigne,
					   &NbMots,
					   &nbv,
					   &nbc));

printf("\nNombre de voyelles : %i\n",nbv);
printf("\nNombre de consonnes : %i\n",nbc);

afficher_nb_mots_par_ligne(nbMotsParLigne);

printf("\nNombre total de mots : %i\n",NbMots);

afficher_nb_occurences(s);
}