Les tableaux
Description du chapitre et des ses objectifs :
Maintenant que vous avez la possibilité de faire à peu près tous les algorithmes que vous souhaitez, nous allons commencer à nous interresser aux
formats de donnée et plus particulièrement aux
tableaux (ou
array en anglais). Vous pourrez ainsi stocker aisément de nombreuses données sans avoir à répéter du code. De plus, vous allez faire vos premiers pas vers des
chaînes de caractères.
Accéder directement à une des parties du cours :
Déclaration d'un tableau
Pour vos premiers pas avec les tableaux, commençons par apprendre à les créer.
Un tableau se déclare de la façon suivante:
Ce n'est peut-être pas assez lisible comme cela, prennons un exemple:
C'est déjà plus clair
Question : Mais qu'est-ce que ça veut dire?
C'est la tout le concept: les tableaux sont donc comme vous avez pu le comprendre, un
nombre détermin de variables d'un type donné, finit de déclarer 50 int à la suite donc! Plus techniquement, un tableau est un
espace mémoire allou, et toute cette mémoire est
contigüe. C'est à dire que tout est à la suite et plus particulièrement, il y a
<taille> fois le nombre d'octets du
<type>. En l'occurrence, sur mon exemple, il a 10 fois (la taille du tableau) un
int (type du tableau), avec un int qui vaut 4 octets. On a donc 40 octets de pris sur la RAM de façon contigüe.
Je suppose que ça ne vous avance pas beaucoup, mais c'est une précision importante pour comprendre ce qu'il va suivre.
Erreur : Les tableaux en C sont fixes, c'est à dire que la
taille du tableau doit être fixe et non variable. Par exemple, il est
impossible de faire:
int i=10;
float tab[i]; //on veut déclarer un tableau de 10 flottants
On est obligé de mettre une taille écriture en "dur". C'est une spécificitée du C qui fait un peu sa faiblesse, mais pour le moment, on s'en accomodera.
Information : Avec la norme C99, ce que je viens de dire n'est pas valable, cependant la taille maximale n'est pas très grande. Pour palier à cela, le C possède l'allocation dynamique, mais vous verrez cela uniquement dans la partie avancée.
Manipulation des tableaux
Maintenant il faut savoir accéder à chaque emplacement de stockage d'un tableau. Pour cela, c'est bien facile:
-les manipulations sur les emplacements de chaque tableau sont les mêmes que celle des variables simples.
-on accède à la Nième case d'un tableau en respectant cette syntaxe:
Voici un exemple si je souhaite accéder à la 6° case:
int tab[10];
tab[5] = 12;
Question : Hein? On accède pas à la 5° case la?
Non! Les
indices d'un tableau vont
TOUJOURS de 0 à
<taille>-1 ; ce qui fait bien
<taille> élément dans un tableau.
Exemples pratiques:
int tab[10]; // tableau de 10 éléments dont les indices vont de 0 à 9
float blabla[541]; // tableau de 541 éléments dont les indices vont de 0 à 540
Il existe une façon d'initialiser rapidement un tableau avec l'utilisation des "{ }". En effet, il suffit de mettre à l'intérieur des accollades les valeurs que l'on souhaite faire prendre à notre tableau (la première valeur ira dans la première case du tableau etc.) , en les séparant valeurs par des virgules. Si le nombre de valeurs entre les accollades est inférieur à la taille du tableau, alors à tous les autres emplacement du tableau sera attribué la valeur 0. Voici un exemple:
int tableau[5]= {2,42,-10};
int i;
for (i = 0; i < 5; i++)
{
printf("tab[%d]=%d ",i,tab
[i
]);
}
Affiche:
tab[0]=2 tab[1]=42 tab[2]=-10 tab[3]=0 tab[4]=0
J'ai parcourut mon tableau et j'ai affiché chacune des valeurs parcourues.
Attention : Il est seulement possible d'utiliser cette méthode lors de la déclaration du tableau. Vous ne pourrez donc jamais voir:
int tableau[10];
tableau[10]= {1,2,3}
C'est TOTALEMENT incohérent, de plus
tableau[10] n'existe pas, je le rappelle,
tableau[9] est la dernière case du tableau.
Attention : Il faudra toujours faire attention à ne pas dépasser la mémoire allouée à un tableau! Ce dépassement se nomme: over buffer flow et provoque des failles de sécurité, fuites de mémoire, changement climatique, etc. Vous comprendrez donc qu'il faudra toujours faire attention à ne pas accéder à une valeur impossible de votre tableau.
N'hésitez pas à regarder la partie d'exercices qui est pleine d'indications importantes.
Tableaux et fonctions
Afin de parfaire votre utilisation des tableaux, il vous faut pouvoir les utiliser dans des fonctions.
Première chose à savoir, contrairement aux variables, on ne va pas envoyer les valeurs contenues dans un tableau, mais l'emplacement mémoire de ce tableau (ou son adresse) et plus particulièrement, l'emplacement mémoire du début du tableau, sa première case. A partir de cela, nous allons pouvoir atteindre le contenu de chacune des cases du tableau souhaité, quelquesoit sa taille.
Voyons déjà comment déclarer une fonction demandant un tableau en argument. Ici pas de problème spécifique, il suffit de mettre le type du tableau, suivit d'un nom de tableau (qui sera celui utilisé à l'intérieur de la fonction) et de mettre deux crochets "[]" au bout. On a aussi l'habitude de demander la taille du-dit tableau. Un petit exemple comme à l'accoutumée, une fonction qui renvois la somme des n ( nombre_elements ) premiers éléments d'un tableau d'entier:
int somme_tableau(int tableau_quelconque[], int nombre_elements)
{
int i, somme = 0;
for ( i=0; i<nombre_elements ; i++)
{
somme = somme + tableau_quelconque[i];
}
return somme;
}
N'hésitez pas à bien regarder le déroullement de l'algorithme sur papier.
Comme vous pouvez le remarquer, il n'est pas necessaire d'indiquer la taille du tableau dans le premier argument de la déclaration de la fonction.
Attention : Ici encore, il faudra faire attention à ce que l'on ne mette pas un nombre_elements plus grand que la taille du tableau.
Voyons maintenant comment utiliser cette fonction.
Si vous vous rappellez de ce que j'ai dit sur
scanf, vous comprendrez plus facilement. On va envoyer l'adresse du premier élément tableau à la fonction. Pour cela, on va utiliser l'
opérateur "&". Il suffit de mettre ce symbole en préfixe à la case du tableau pour en avoir l'adresse. Petit exemple:
#include <stdio.h>
#include <stdlib.h>
int somme_tableau(int tableau_quelconque[], int nombre_elements)
{
int i, somme = 0;
for ( i=0; i<nombre_elements ; i++)
{
somme = somme + tableau_quelconque[i];
}
return somme;
}
int main()
{
int tab[10]={ 5, 22, -40, 79, 6, 42};
printf("Somme des 4 premiers termes= %d\n", somme_tableau
( &tab
[0],
4 ) );
/* On envoit l'adresse de la case 0, et on renvois la somme des 4 premières cases à partir de la case 0 */
printf("Somme du troisième au sixème terme= %d\n", somme_tableau
( &tab
[2],
3 ) );
/* On envoit l'adresse de la case 2 (donc la troisième en comptant la case 0) qui sera considéré à l'intérieur de notre fonction comme étant la case 0. A partir de celle-ci nous allons donc faire la somme des 3 premières valeurs de notre "nouveau" tableau */
return 0;
}
Comme vous pouvez le voir, on peut envoyer l'addresse de n'importe quelle case du tableau. Celle ci sera considéré commme la case 0 du tableau pour la fonction à laquelle on envoit l'adresse.
Attention : Encore une fois, faites bien attention à ce que vous ne dépassiez pas le la taille originelle de votre tableau, sinon votre programme plantera.
Question : Est-ce que je peux retourner l'adresse d'un tableau?
Oui! Mais je ne vous montrerez pas cette technique pour le moment, c'est une technique plus avancée qui n'est pas encore appropriée pour le moment, lisez le commentaire juste en bas pour plus d'informations. De plus, vous ne pouvez pas pour le moment retourner de tableaux.
Information : Il existe d'autres méthodes de manipulation des tableaux que nous verrons avec les pointeurs dans la partie avancée, pour le moment nous allons nous contenter de cela.
Les chaînes de caractères
Les tableaux ont aussi une autre utilité pratique: gérer des chaines de caractères.
Question : Il y a un type pour stocker des lettres?
Non, c'est plus subtile que ça. En informatique, tout n'est que chiffre, alors le problème qui s'est posé, c'est quelles valeurs attribuer à des lettres. Pour cela, il y a plusieurs normes, mais en régle générale, on utilise l'
ASCII.
Ce que l'on va donc faire, c'est stocker des chiffres, et pour chaque chiffre, à l'affichage, une fonction s'occupera de convertir (ce que fait
printf par exemple). En ASCII de base, on utilise 256 caractères différents, soit la taille d'un
char. On va donc créer des tableaux de
char pour stocker des chaînes de caractères. (
char tient d'ailleurs son nom de character, caractère en anglais ). Voilà comment créer une chaîne de caractères:
Bref, c'est exactement un tableau. Le problème me direz vous, c'est de remplir ce tableau. Pour cela il existe une méthode à l'initialisation: pour déclarer une chaine de caractères facilement, on utilise des guillements, comme on l'a vu avec
printf. Voici un exemple rapide:
Que va contenir le tableau
foo? Et bien:
foo[0] = 'S';
foo[1] = 'a';
foo[2] = 'l';
foo[3] = 'u';
foo[4] = 't';
foo[5] = '!';
foo[6] = 0;
foo[7] = 0;
foo[8] = 0;
foo[9] = 0;
Attention : Il y a 2 choses importantes à remarquer dans cet exemple:
1° Lorsque en C on mets des apostrophes '' cela signifie que c'est un caractère, et que l'on donne la valeur de ce caractère en chiffres. Pour connaître la valeur de chaque lettre, reportez vous à ce site:
http://www.table-ascii.com/ Vous remarquerez donc que le 0 contenu dans les dernières cases de foo ne sont pas des lettres '0' mais bien le chiffre 0. En ASCII, '0' vaut 48.
2° Comme lorsque l'on a utilisé les "{}" pour initialiser le tableau, les cases non utilisées sont remplies de 0. Vous verrez qu'elles ont leur utilitée.
Attention : Veillez à ne pas confondre les guillements "" qui servent à déclarer une chaine de caractères, soit un tableau, contrairement aux apostrophes '' qui servent à donner la valeur d'un caractère.
Il existe une autre forme un peu plus dynamique pour créer une chaîne de caractère, il s'agit de ne pas spécifier la taille de la chaîne de caractères entre les crochets. Le tableau va automatiquement créer un tableau de la taille de la chaîne de caractère +1 ! En effet, le tableau va mettre un 0 à la fin de la chaîne.
Question : Quelle est l'utilitée du 0 à la fin de la chaîne?
En fait, cela sert essentiellement aux fonctions, cela leur permet de savoir quand arrêter de travailler sur la chaîne sans en connaître la taille, contrairement à ce que l'on a fait avec la fonction
somme_tableau. La fonction va simplement s'arrêter quand elle rencontre un 0. Vous pourrez utiliser aussi cette astuce avec le nombre que vous souhaitez avec vos tableaux (attention quand même à l'utilisation et encore une fois à ne pas dépasser du tableau).
Voici donc l'utilisation d'une chaîne de caractères avec la fonction
printf:
char chaine[]="Salut les gens"
printf("%s\n", &chaine
[0]);
Comme vous pouvez le voir, ici encore pour utiliser un tableau dans une fonction, on est obligé d'envoyer l'adresse du tableau. Aussi on utilise le formateur
%s pour déclarer que l'on attend en argument une chaîne de caractères (le s vient de
string en anglais)
Question : Comment afficher seulement un caractère?
Pour celà
printf utilise le formateur "%c" qui écrit le caractère correspondant à la valeur ASCII, si par exemple je veux écrire les caractère 'T' 'Z' et 'W', peux faire de cette façon:
char lettre_T = 'T';
char lettre_W = 'W';
char lettre_Z = 90;
printf(" %c%c%c \n",lettre_T, lettre_Z, lettre_W
);
Encore une fois, 'T' 'b' '*' ne sont que des valeurs numériques qui sont, grâce à
printf, converties en caractère correspondant à la table ASCII.
Vous pouvez de même essayer de remplacer la dernière ligne par:
printf("'T'=%d 'Z'=%d 'W'=%d\n",lettre_T, lettre_Z, lettre_W
);
scanf permet aussi de récupérer des chaînes de caractères, son utilisation est à peu près identique:
char buffer[50];
scanf("%s", &buffer[0] );
Attention : Ici se trouve une des faiblesses de scanf, en effet, vous ne pourrez pas prendre une chaîne de caractères avec des espaces. De plus il faudra faire très attention à ce que l'on écrit ne sorte pas de la taille du tableau! Faites très attention à ce point.
Information : Il existe beaucoup de fonctions de manipulation de chaîne de caractères, en voici quelques une:
-strncpy
-atoi
-sprintf
Je vous laisse aller chercher sur google leur manuel (man <nom_fonction> )
Concernant les caractères, une autre chose est à savoir: l'utilisation d'un antislash \ suivit d'un nombre EST un caractère! '\0' est le caractère correspondant au nombre 0, soit rien... '\87' est le caractère 87, soit le 'W'. C'est pour cela que vous trouverez souvent cette notation:
while( caractere_en_cours != '\0' ) //tant que le caractere n'est pas égal 0 signifiant une fin de chaîne
Tableaux à plusieurs dimmensions
Vous pouvez aussi être ammené à cotoyer des tableaux à plusieurs dimmensions, ce qui signifie tout simplement que pour chaque case d'un tableau se trouve un autre tableau, pour un tableau à 2 dimensions (et chaque case de ce tableau peut contenir un tableau et ainsi de suite, ces tableaux sont aussi appelés matrices ou vecteurs par les mathématiciens)
Pour déclarer un tableau de ce type, il suffit de coller de nouveau les crochets [] contenant la taille de la nouvelle dimmension. Exemple:
Vous pouvez accéder à la 4° colonne de la 1° ligne de cette façon:
tableau_2D[0][3] = 50; //je rappelle que tout tableau commence à 0
La seule grosse différence vient de leur utilisation dans la déclaration de la fonction. En effet, dans la mémoire de votre ordinateur, le tableau aura tout de rangé côte à côte, 2 dimmensions n'existent pas. Pour palier à cela, il faut que la fonction sache de combien de cases elle doit faire un saut pour accéder à la ligne suivante. Ce nombre de cases correspond au nombre de colonnes de chaque ligne.
Voici donc un exemple:
void fonction_inutile(int tableau_taille_specifique[10][], int lignes, int colonnes)
{
;
}
Comme vous pouvez le constater, ce n'est pas très pratique. Cependant, vous verrez bientôt comment palier à ce problème. L'appel de cette fonction s'effectue comme pour un tableau normal:
fonction_inutile(&tableau2D[0][0],10,10);
Une fois que avez découvert les tableaux, n'hésitez pas à en abuser. La combinaison boucle/tableau vous permettra de stocker énormément d'informations très facilement et de récupérer avec beaucoup d'aisance.
Chapitre précédent - Sommaire - Chapitre suivant
Nos rédacteurs et membres sont pour la plupart ouverts à des remarques constructives et servir à alerter le rédacteur du cours, des fautes éventuelles ou de propositions et nouvelles perspectives de cours etc ...
Pour ce faire cliquez ici
Postez vous aussi un commentaire à cette partie via le lien que voici