CINI – Li115 1 Semaine 5 Les tableaux ● Qu'est-ce qu'un tableau ? ● Déclarer et initialiser un tableau ● Fixer la taille d'un tableau ● Opérations classiques ● - Parcours d'une section fixe de tableau Affichage, recopie d'un tableau, recherche dans un tableau non trié ● - Parcours incomplet du tableau ● Recherche dans un tableau trié, tableau partiellement rempli ● - Echange du contenu de 2 cases ● Représentation graphique d'un tableau ● Tableau à deux dimensions
CINI – Li115 2 Qu'est-ce qu'un tableau ? ● Tableau = ensemble ● - de taille fixe ● - de variables du même type ● - adressées par un indice (ou numéro) : leur position dans le tableau ● Exemples : ● - liste des classes énergétiques→ tableau de caractères ● - numéros de carte des étudiants d'un groupe de TD ● →tableau d'entiers ● - notes d'un étudiant →tableau de réels ● - noms des étudiants d'un groupe de TD →tableau de chaînes de caractères
CINI – Li115 3 Déclarer un tableau ● Pour que le compilateur puisse travailler, il faut fixer à la déclaration : ● - le nom du tableau ● - sa taille (le nombre maximum d'éléments qu'il peut contenir) ● - le type des éléments qu'il contient ● type_elem nom_tab[taille_tab]; ● représente un ensemble de taille_tab variables – chaque variable est de type type_elem – les noms des variables sont nom_tab[0], nom_tab[1], …, nom_tab[taille_tab – 1]
CINI – Li115 4 Exemple ● char classe_energie[5] = {'A', 'B', 'C', 'D', 'E'}; 'E ' 'D ' 'C ' 'B ' 'A ' classe_energie classe_energie[0] a la valeur 'A' CINI_print_char(classe_energie[3]); affiche le caractère D
CINI – Li115 5 Initialiser un tableau ● On peut affecter une valeur à chaque case indépendamment : ● → instructions dans le corps du programme – nom_tab[i] = val; – affecte la valeur val à la case d'indice i du tableau nom_tab (avec 0 <= i < taille_tab) – val est une expression de type type_elem ● Attention : ● 1) En C, les indices d'un tableau commencent toujours à 0 ● 2) Quand on déclare un tableau sans initialiser le contenu des cases, celui-ci est indéterminé
CINI – Li115 6 ● Première possibilité : ● type_elem nom_tab[taille_tab] = {val1, val2, … }; ● La liste contient au maximum taille_tab valeurs ; ● Si elle en contient plus → erreur de compilation – warning: excess elements in array initializer ● L'affectation des valeurs se fait dans l'ordre : – nom_tab[0] = val1, nom_tab[1] = val2,... ● Si la liste est incomplète, le contenu des cases non initialisées est arbitraire ● Deuxième possibilité : ● type_elem nom_tab[] = {val1, val2, val3}; ● La taille du tableau est donnée par la taille de la liste. Elle n'est pas modifiable. Initialiser un tableau à la déclaration
CINI – Li115 7 Déclaration et initialisation ● #include ● int main() { ● float tab_f[3] = {3.1, 2.2}; ● int tab_ent[4]; ● tab_ent[0] = 17; ● CINI_print_string("tab_ent[0]="); ● CINI_print_int(tab_ent[0]); ● CINI_print_string("tab_ent[1]="); ● CINI_print_int(tab_ent[1]); ● CINI_newline(); ● CINI_print_string("tab_f[2]="); ● CINI_print_float(tab_f[2]); ● CINI_newline(); ● return 0; ● } ● A la compilation : ● warning: ‘tab_ent[1]’ is used uninitialized in this function ● Le compilateur ne détecte pas que tab_f[2] n'est pas initialisé non plus ● A l'exécution : ● tab_ent[0]=17 ● tab_ent[1]=0 ● tab_f[2]= ● On peut trouver n'importe quelle valeur dans tab_ent[1] et tab_f[2]
CINI – Li115 8 Fixer la taille d'un tableau ● Il est interdit d'utiliser une variable pour dimensionner un tableau ● int taille = 3; ● int tab1[taille]= {1, 2, 3}; ● error: variable-sized object may not be initialized ● Dans certains cas (tableau non initialisé, variable locale...), le compilateur l'acceptera... ● → mais c'est MAL !
CINI – Li115 9 Fixer la taille d'un tableau avec #define ● #define permet d'associer une valeur à un identificateur ● On ne peut pas effectuer d'opérations sur l'identificateur ● → la valeur correspondante est constante ● #define TAILLE 3 ● … ● int tab[TAILLE];→ int tab[3]; ● Pourquoi est-ce mieux que d'utiliser 3 directement ?
CINI – Li #define vs valeur ● #define permet de modifier facilement la taille du tableau dans TOUT le programme et de ne modifier QUE la taille du tableau (et pas les autres variables initialisées à 3). ● Exemple: #define TAILLE 3 ● … ● int tab[TAILLE]; ● int var = 3, i; ● for(i=0;i<TAILLE;i++) { … ● → En modifiant la première ligne, on modifie la taille du tableau partout, sans modifier la valeur de var. Conseil : Puisque le nombre de cases est modifié, attention à l'initialisation du tableau ! int tab[3]; int var = 3, i; for(i=0;i<3;i++) { … À comparer avec:
CINI – Li Erreurs d'accès aux éléments d'un tableau ● #define TAILLE 3 ●... ● int tab[TAILLE]; ● Le compilateur ne fait pas de vérification sur les indices ● tab[-1] = 1;→ pas d'erreur à la compilation ● Pas de marqueur de fin de tableau ● tab[3] = 3;→ pas d'erreur à la compilation ● Dans les deux cas, résultat imprévisible à l'exécution tab[0]tab[2]tab[1] ? ? Conseil : toujours vérifier que 0 ≤ indice ≤ TAILLE -1
CINI – Li Tableaux de caractères ● char tab [ ] = {'a', 'b', 'c'}; ● Le tableau contient 3 cases : – tab[0] a la valeur 'a' – tab[1] a la valeur 'b' – tab[2] a la valeur 'c' char tab [ ] = ''abc'' ● Le tableau contient 4 cases ● tab[0], tab[1] et tab[2] ont la même ● valeur que dans la déclaration précédente tab[3] a la valeur '\0' la notation '\0' représente un caractère spécial marquant la fin d'une chaîne de caractères 'a' 'b' 'c' 012 tab 'a' 'b' 'c' 012 tab 3 '\0'
CINI – Li Parcours d'une section fixe de tableau ● Parcours d'une section de taille N connue, N ≤ taille du tableau ● → boucle for – On ne parcourt pas forcément tout le tableau – La section parcourue n'est pas forcément au début du tableau – Exemples : affichage (partiel) d'un tableau, recopie (partielle) d'un tableau, recherche du plus petit élément d'un tableau non trié (et de son indice) ● Attention aux débordements ● Vous devez vous assurer que vous ne sortez pas du tableau
CINI – Li Affichage du contenu d'un tableau #include #define TAILLE 4 #define N 3 int main() { float tab[TAILLE] = {1.7, -2.34, 0.0, -7.65}; int i; for (i = 0; i < N; i++) { CINI_print_float(tab[i]); CINI_print_string(" "); } CINI_newline(); return 0; } Résultat d'exécution :
CINI – Li Recopie d'un tableau (1) ● Le type tableau n'existe pas ● → pas d'opérateur pour affecter un tableau dans un autre ● int tab1[3] = {1, 2, 3}; ● int tab2[3]; ● tab2 = tab1; ● À la compilation : incompatible type in assignment ● Pour copier un tableau dans un autre, il faut écrire une boucle
CINI – Li Recopie d'un tableau (2) #include #define N 4 int main() { int tab1[N]= {1, 2, 3, 4}; int tab2[N]; int i; for (i=0; i<N; i++) { tab2[i] = tab1[i]; } return 0; } Attention : vérifier que tab2 est assez grand ! tab1 tab2 Sinon, la copie peut écraser des informations en mémoire
CINI – Li Recherche du minimum dans un tableau non trié Il faut parcourir tout le tableau L'algorithme utilise une variable auxiliaire pour stocker : ● → soit la valeur du minimum sur les cases déjà parcourues : elle est initialisée avec la valeur contenue dans la première case ● → soit la position (numéro de case) du minimum : elle est initialisée à 0 Pour chaque nouvelle case examinée ● → on compare la valeur de son contenu avec le minimum courant ● → si la valeur est plus petite, on met à jour la variable auxiliaire A la fin du parcours, la variable auxiliaire contient le résultat
CINI – Li ● #include ● #define N 8 ● int main() { ● int tab[N] = {1, 3, 7, -2, 4, -5, 0, 9} ; ● int i, pos_min = 0; ● for (i = 1; i < N; i++) { ● if (tab[i] < tab[pos_min]) { ● pos_min = i ; ● } ● CINI_print_string("Le minimum est dans la case "); ● CINI_print_int(pos_min); ● CINI_print_string(" et sa valeur est : "); ● CINI_print_int(tab[pos_min]); ● CINI_newline(); ● return 0; ● }
CINI – Li Parcours incomplets de tableaux ● Certaines opérations ne parcourent qu'une section de tableau ● la taille de la section peut ne pas être connue a priori ● - parce qu'on fait une recherche qui aboutit avant d'avoir parcouru tout le tableau ● - parce que toutes les cases du tableau ne contiennent pas des valeurs significatives ● → boucle while ● Attention à ne pas dépasser la fin du tableau
CINI – Li Recherche d'une valeur particulière dans un tableau ● On continue la recherche tant que : – - on n'a pas atteint la fin du tableau ET – - on n'a pas trouvé l'élément cherché La condition de continuation porte dans l'ordre ● - sur la taille du tableau ● - sur la valeur de l'élément recherché Il faut déterminer en fin de parcours si on a trouvé la valeur ou non
CINI – Li ● #include ● #define N 3 ● int main() { ● int val = 5, i = 0; ● int tab[N] = {1, 2, 4}; ● while ((i < N) && (tab[i] != val)) { ● i++; ● } ● if (i == N) { ● CINI_print_string("Element non trouvé"); ● } else { ● CINI_print_string("Element trouvé"); ● } ● CINI_newline(); ● return 0; ● }
CINI – Li Exécution du programme de recherche ● i = 0 ● i < N ? vraitab[0] ≠ val ? vrai→ i++ ● i = 1 ● i < N ? vraitab[1] ≠ val ? vrai→ i++ ● i = 2 ● i < N ? vraitab[2] ≠ val ? vrai→ i++ ● i = 3 ● i < N ? fauxdonc la condition de continuation est fausse – → la condition tab[3] ≠ val n'est pas évaluée – → on ne va pas lire tab[3] qui est en dehors du tableau
CINI – Li Tableaux partiellement remplis ● Le tableau ne contient des valeurs significatives que jusqu'à un certain indice. ● → les cases au delà de cet indice ne sont pas vides, mais leur contenu est indéterminé ● Comment identifier la partie significative du tableau ? ● - soit on utilise une valeur particulière pour marquer la fin des éléments significatifs du tableau. Cette valeur est du même type que les éléments significatifs mais ne doit pas pouvoir être confondue avec eux. ● - soit on mémorise lors du remplissage le dernier indice des cases auxquelles on a affecté une valeur
CINI – Li Exemples ● - parcours d'un tableau de notes : ● une note est un nombre flottant ≥ 0 ; ● on stocke une valeur négative dans le tableau dans la case qui suit la dernière note ; ● il est possible cependant que toutes les cases contiennent des notes → arrêt lorsqu'on atteint la dernière case ● - parcours d'une chaîne de caractères : ● char tab[] = "j'ai bien fait de venir en cours !" ; ● le caractère '\0' est automatiquement ajouté en fin de chaîne ● plutôt que de calculer la taille, on parcourt tant que tab[i] != '\0'
CINI – Li Affichage d'un tableau de notes partiellement rempli #include #define N 8 int main() { float tab[N] = {11.5, 8.0, 12.75, -1} ; int i = 0; while ( (i = 0) ) { CINI_print_float(tab[i]); CINI_print_string(" "); i++ ; } CINI_newline(); return 0; }
CINI – Li Echanger le contenu des cases d'indices i et j ● Retenir : On a besoin d'une variable auxiliaire 5 5 i j tab[i] = tab[j]; 1 5 i j aux tab[j] = tab[i]; 1 5 i j 1 aux = tab[i]; 5 tab[i] = tab[j]; tab[j] = aux; 1
CINI – Li Représentation graphique d'un tableau En utilisant la bibliothèque graphique Une case du tableau est de dimensions 80 x 50 → il faut dimensionner la fenêtre en conséquence → une valeur numérique ne peut pas être représentée avec plus de 5 chiffres Il faut préciser : - les coordonnées (x, y) du point en haut à gauche - le tableau à afficher - le nombre de cases à afficher - la couleur utilisée pour le fond des cases (bg_color) - la couleur du crayon (fg_color), utilisée pour le texte et les bordures
CINI – Li Les fonctions d'affichage Une fonction par type de données : Tableau de caractères : void CINI_draw_char_table(int x, int y, char tab[ ], int size, string bg_color, string fg_color);Tableau de booléens : void CINI_draw_bool_table(int x, int y, bool tab[ ], int size, string bg_color, string fg_color);Tableau d'entiers : void CINI_draw_int_table(int x, int y, int tab[ ], int size, string bg_color, string fg_color);Tableau de réels : void CINI_draw_float_table(int x, int y, float tab[ ], int size, string bg_color, string fg_color);
CINI – Li Un exemple simple... #include int main() { char tab[]= "Li115"; CINI_open_window(600, 100, "tabchar"); CINI_fill_window("light grey"); CINI_draw_char_table(10, 10, tab, 5, "white", "black" ); CINI_loop(); return 0; }
CINI – Li … qui affiche CINI_draw_char_table(10, 10, tab, 5, "white", "black" ); CINI_draw_char_table(10, 10, tab, 6, "white", "black" ); (10,10) '\0'
CINI – Li Et si on dépasse la taille du tableau ? CINI_draw_char_table(10, 10, tab, 7, "white", "black" ); n'importe quoi
CINI – Li Tableaux à deux dimensions ● Pour représenter des ensembles de données : ● - présence de tous les étudiants d'un groupe à l'ensemble des séances de TD ● - plateaux de jeux : bataille navale, scrabble, sudoku, etc. ● Il faut fixer le type des éléments, le nombre de lignes et le nombre de colonnes : ● type nom_tab[nb_lignes][nb_colonnes]; ● Exemples : bool presences[NB_ETU][NB_SEANCES]; ● int sudoku[9][9]; ● char scrabble[15][15];
CINI – Li Exemple : match de tennis en 3 sets ● 2 lignes → #define NB_L 2 ● 3 colonnes → #define NB_C 3 ● int tab[NB_L][NB_C]; ● Combien y a-t-il eu de jeux joués ? ● Combien Legrand a-t-il gagné de jeux ? Legrand Lepetit
CINI – Li Parcours d'un tableau à deux dimensions #include #define NB_L 2 #define NB_C 3 int main() { int tab[NB_L][NB_C]; int i, j; int nb_jeux = 0; int nb_jgrand = 0; tab[0][0] = 6; tab[0][1] = 5; tab[0][2] = 2; tab[1][0] = 3; tab[1][1] = 7; tab[1][2] = 6; for (i = 0; i < NB_L; i++) { for (j = 0; j < NB_C; j++) { nb_jeux = nb_jeux + tab[i][j]; } CINI_print_string("Il y a eu "); CINI_print_int(nb_jeux); CINI_print_string(" jeux joues dans le match"); for (j = 0; j < NB_C; j++) { nb_jgrand = nb_jgrand + tab[0][j]; } CINI_print_string(" et Legrand a gagne "); CINI_print_int(nb_jgrand); CINI_print_string(" jeux."); CINI_newline(); return 0; }