Programmation en c

Fermé
nedjma - 10 févr. 2008 à 10:46
Mahmah Messages postés 496 Date d'inscription lundi 17 septembre 2007 Statut Membre Dernière intervention 22 juin 2010 - 11 févr. 2008 à 21:32
Bonjour,je suis debutante dans la programmation en c je dois utiliser pour mon programme soit des listes chainnées soit des tableaux 2 et 3 dimensions d'aprés vous quels sont les avantages de chacun .ou je pourais trouver la documentation qui poura m'aider à utiliser les tableaux multidimentionnels l'allocation dynamique je ne comprends pas trés bien!
A voir également:

4 réponses

fiddy Messages postés 11069 Date d'inscription samedi 5 mai 2007 Statut Contributeur Dernière intervention 23 avril 2022 1 844
10 févr. 2008 à 10:50
lliste chainée dans google : premier lien te donne https://www.commentcamarche.net/contents/114-langage-c-les-listes-chainees
0
Mahmah Messages postés 496 Date d'inscription lundi 17 septembre 2007 Statut Membre Dernière intervention 22 juin 2010 125
10 févr. 2008 à 12:29
Bonjour,

Les grandes différences entre les tableaux et les listes chaînées sont avant tout l'utilisation dont on a besoin. La liste chaînée est de manière générale plus lente d'accès (accès trop souvent séquentiel) mais permet par contre ajouter et de supprimer très facilement des éléments. Un tableau est un accès indirect à la mémoire (lire les données à "Adresse de départ + indice") par contre une fois qu'il est plein... Il faut en reconstruire un plus grand. (et ce n'est pas rien)

Deux cas typique sont:
- Gérer un objet contenant une chaîne de caractères saisie caractère par caractère.
- Mémoriser l'identifiant des personnes actuellement dans le batiment sécurisé.

Dans le premier cas on pourra faire avec un tableau et chaque fois qu'il sera trop petit on doublera sa taille.
Dans le deuxième je ferais une liste chaînée car chaque fois qu'une personne sort on ne fait pas un trou en plein milieu des données vue qu'elles sont automatiquement recousues entre-elles.

Pour allouer des tableaux dynamiquement il faut utiliser la fonction malloc ou calloc si tu souhaites que la mémoire soit initialisée à zéro en même temps. Contrairement à ce que dit l'exemple il est préférable (/obligatoire) de caster le pointeur renvoyé même en C. Et biensûr le free pour libérer la mémoire réservée lorsque l'on en a fini avec elle.

Pour allouer les tableaux à plusieurs dimensions, il y a deux écoles.

L'allocation par dimension:
On alloue un premier tableau
int ***pppiTab = (int*) malloc( nElements * sizeof( int** ) );

Pour chaque sous éléments on alloue un autre talbeau
for ( unsignend int i = 0 ; i != nElement2 ; i++ )
   pppiTab[i] = (int **) malloc( nElement2 * sizeof( int * ) );

Et on recommence pour la troisième dimension.

Ou alors,
On alloue un seul gros bloc pour tout le tableau
int *piTab = (int *) malloc( nElements1 * nElements2 * nElements3 * sizeof( int ) );

Mais il faut alors une logique plus complexe pour accéder aux éléments.
// piTab[2][3][4]
piTab[ 2 * nElements1 * nElements2 + 3 * nElements2 + 4 ];
Il est donc préférable pour cette méthode de faire une fonction d'accès aux éléments.

Pour la liste chaînée le lien ci-dessus est certainement très bien.

Pour faire ton choix il te faut donc voir comment tu vas utiliser tes données:
- Toujours toute les parcourir pour les traiter ou accéder à une directement via un indice.
- Le nombre de données est-il stable au cours du coeur du programme ? (si c'est juste pour l'initialiser et qu'après il ne bouge pendant le traitement ou varie constamment sans limite connue.)

Voilà, n'hésite surtout pas en cas de besoin ;)
M.
0
merci de m'avoir répondu.
j'ai tjrs le meme probleme je n'arrive pas à comprendre comment utiliser des tableaux dynamiques multidumentionnels je cherche une documentation en francais car je ne métrise pas l'anglais.je voudrais comprendre l'allocation par dimension.les ** et les *** je n'arrive pas à comprendre.
0
Mahmah Messages postés 496 Date d'inscription lundi 17 septembre 2007 Statut Membre Dernière intervention 22 juin 2010 125
11 févr. 2008 à 21:32
Re bonjour,

Bon j'ai cherché vite fait, je n'ai pas trouvé de lien qui soit vraiment bien.

J'avais commencé une explication de base des pointeurs mais je me suis dis que j'étais à côté de la question, dis moi si ça t'intéresse et je la ferai ;-)

Pour les tableaux dynamiques...
L'idée est qu'on ne connaît leur taille et on peut donc imaginer un programme qui demande un nombre d'entier puis saisit ces entiers dans un tableau. On récupère la taille via un scanf et on ira même jusqu'à l'appeler N. A partir de là il nous faut de la mémoire car on sait qu'on attend N entiers. On va donc demander à notre charmant système d'exploitation si il serait cap' de nous prêter un peu de mémoire. Il nous réserve un bloc de la taille demandée et nous dit où il est, son adresse en somme. Quand on en aura plus besoin, on le lui fera savoir parce que nous aussi on est trop chous avec lui. ^^

Pour cela:
void * malloc( size_t );
void   free();


malloc va réserver le nombre d'octects passé en paramètre, soit pour nous : N * la taille en octect d'un entier, soit encore N * sizeof( int )
Quand il aura trouvé, il renverra l'adresse vers le début du bloc qu'il nous a mis au chaud. (Et si il est de mauvais poil il refuse d'un NULL)
Le void * en retour est un pointeur... certains disent que void est "non-typé", d'autre disent que void c'est "rien", moi je dis void c'est "quelque chose". Toujours est-il que nous avons un bloc et que on veut y mettre des entiers, donc on caste en pointeur vers des entiers.
(En C++ ce type de caste s'appelle le reinterpret_cast: et effectivement on change notre façon d'interpréter un objet)

int   *p = (int *) malloc( N * sizeof( int ) );

if ( p != NULL )
{
   // Pouet pouet
   ...

   free( p );
}


On a un "tableau" p qui peut contenir N entiers.
On accède au éléments avec:
p[0] .... p[N-1]
*(p + 1) ... *(p + N-1)
Et c'est là qu'il est important de savoir non pas que p est un pointeur vers le début d'un bloc de N * sizeof( int ) octects mais sur N entiers. Car p + 1 est l'adresse du deuxième entier et non du deuxième octect. La multiplication par la taille de l'élément pointé est faite automatiquement : c'est "l'arithmétique des pointeurs".

Bon, pour en revenir à tes moutons...
typedef struct { int beeeeh; } t_mouton;

Nous allons faire un tableau dynamique à deux dimensions de t_moutons.
Ce qu'on demande ici explicitement, c'est de pouvoir stocker N * M t_moutons. Pour cela on va alors demander N blocs mémoires qui peuvent chacun contenir M t_moutons. Donc bien au final N * M t_moutons. Ces N blocs pouvant contenir M t_moutons sont chacun désigné par leur adresse. (Et il y a peu de chance que ces blocs se suivent dans la mémoire) Pour garder ces N adresses, on va les mettre dans un tableau qui peut contenir... N adresses vers des blocs contenant des t_moutons.

t_mouton *p; // est un pointeur vers une zone mémoire qui contient un ou des t_moutons.


t_mouton **ppNosMoutons; // est un pointeur vers un bloc en mémoire qui contient des pointeurs (/des adresses) vers de bloc qui eux contiennent nos t_moutons. C'est bien ça qu'on veut ici.

// On demande donc de la mémoire pour retenir les N adresses qu'on va lui demander après.

ppNosMoutons = (t_moutons **) malloc( N * sizeof( t_moutons *) );
// C'est bien ce que l'on veut : l'adresse d'un bloc pouvant contenir les adresses 
//des blocs de t_moutons,
// et on en veut N. (Soit en octects : N * sizeof( t_moutons *))

EDIT:
Et là ? Qu'est-ce qu'on dit ? ET ouiiiiiiii, y a une grosse faute sur le post du dessus dans le premier bout de code !
(Ah bah bravo, c'est malin... après on s'étonne que les gens on du mal à suivre...)
Ouep le monsieur il a mis un int * dans son int ***
Je suis désolé Nedjma, j'espère ne pas voir semé la confusion :'(
FIN EDIT.

Maintenant on a un moyen de retenir les adresses de nos t_moutons, on va donc demander les N blocs de M t_moutons.
for ( unsigned int i = 0 ; i != N ; i++ )
{
   ppNosMoutons[i] = (t_moutons *) malloc( M * sizeof( t_moutons ) );
   // Et avec ça on obtient N blocs de M t_moutons
}


Quand on en aura fini avec nos moutons (snif les moutons) Mais non, on les libère !
for ( unsigned int i = 0 ; i != N ; i++ )
{
   free( ppNosMoutons[i] );   // on libère les N paquets de M moutons
}

free( ppNosMoutons ); // puis on libère le tableau d'adresses vers nos ex t_moutons.

C'est la même logique mais en sens inverse.

---

Trois dimensions ?

Il nous faut M * N * P petits bateaux (Nan, allez, promis j'arrête, on prend des int)

Deux façons de voir dirais-je...
1) Et si nos t_moutons étaient... des tableaux de int... ^_^
typedef int *t_moutons;

On sait déjà allouer des tableaux de tableaux de t_moutons, on alloue en plus nos t_moutons/tableaux de int.

Pour chaque t_moutons déjà alloués... nous donnons un valeur à nos t_moutons. Une valeur c'est une adresse vers un bloc qui contient des int.
for ( unsigned int i = 0 ; i != N ; i++ )
{
   for ( unsigned int j = 0 ; j != M ; j++ )
   {
      ppNosEntiers[i][j] = (int *) malloc( P * sizeof( int ) );
   }
}


2) On reprend au début...
Il nous faut N * M * P entiers.
on va allouer des bloc de P entiers, on en allouera M, et des bloc de M * P entiers on en veut N.
Garçon, l'addition !
Ou plutôt, la vision dans l'autre sens, c'est selon les goûts.
j'ai un int ***. finalement c'est un tableau de int **
int ***ppp = (int ***) malloc( N * sizeof( int ** ) );

On notera que ppp est un int ***, on réinterprétè notre bloc comme un int *** ("What else ?" comme dirait George...)

pour chaque case de notre beau tableau tout frais tout neuf, il nous faut... des int ** (C'est bien ce qu'on vient de demander juste au dessus: un bloc pour cnotenir N int**)
for( ... )
{
   ppp[i] = (int **) malloc( M * sizeof( int * ) );
}

On notera encore ici que si ppp est un int ***, alors ppp[i] est un int ** ET dans un tableau de type int ** on met des éléments de type int *

Il reste encore une troisième passe à faire pour allouer enfin des blocs de int...

for( ... )
{
   for( ... )
   {
      ppp[i][j] = (int *) malloc( P * sizeof( int ) );
   }
}


Remarque: ceci marche aussi pour les t_moutons ***
Désolé ^^"

M.

PS:
A noter qu'un notation genre la notation hongroise (Le wikipédia français est un peu pauvre malheureusement, l'anglais est mieux) peut aider. On pourra alors préfixer une variable de type int *** par un pppi, ainsi rien qu'au nom de la variable on sait combien d'indirection on a. Il est donc aussi immédiat : pppi[i] est un int **.
(Ah nan, je redeviens sérieux ! Ahhhhhhh !)


(oui, je crie avec plein de 'h', ça fait moins de bruit qu'avec des 'a'...)
0