CONNEXION
  • RetourJeux
    • Sorties
    • Hit Parade
    • Les + populaires
    • Les + attendus
    • Soluces
    • Tous les Jeux
    • Gaming
  • RetourActu Gaming
    • News
    • Astuces
    • Tests
    • Previews
    • Toute l'actu gaming
  • RetourBons plans
    • Bons plans
    • Bons plans Smartphone
    • Bons plans Hardware
    • Bons plans Image et Son
    • Bons plans Amazon
    • Bons plans Cdiscount
    • Bons plans Decathlon
    • Bons plans Fnac
    • Tous les Bons plans
  • RetourJVTech
    • Actus High-Tech
    • Intelligence Artificielle
    • Smartphones
    • Mobilité urbaine
    • Hardware
    • Image et son
    • Tutoriels
    • Tests produits High-Tech
    • Guides d'achat High-Tech
    • JVTech
  • RetourCulture
    • Actus Culture
    • Culture
  • RetourVidéos
    • A la une
    • Gaming Live
    • Vidéos Tests
    • Vidéos Previews
    • Gameplay
    • Trailers
    • Chroniques
    • Replay Web TV
    • Toutes les vidéos
  • RetourForums
    • Hardware PC
    • PS5
    • Switch 2
    • Xbox Series
    • Switch
    • Pokemon pocket
    • FC 25 Ultimate Team
    • League of Legends
    • Tous les Forums
  • PC
  • PS5
  • Xbox Series
  • Switch 2
  • PS4
  • One
  • Switch
  • iOS
  • Android
  • MMO
  • RPG
  • FPS
En ce moment Genshin Impact Valhalla Breath of the wild Animal Crossing GTA 5 Red dead 2
Liste des sujets

Liste chainée... votre avis ...

gulius44
gulius44
Niveau 9
29 mars 2006 à 16:58:29

Voilà, j´ai écris une première éboche d´une classe implémentant une liste chainée (gérant le type int pour le moment, mais ça on s´en fou ...).

Voici le code :
http://membres.lycos.fr/gulius44/c_cpp/test.cpp

Et voici ce que j´ai en retour :
Adresse_objet (0x804b008); _value(1); adresse_objet_suivant (0x804b018);
Adresse_objet (0x804b018); _value(2); adresse_objet_suivant (0x804b028);
Adresse_objet (0x804b028); _value(3); adresse_objet_suivant (0x804b038);
Adresse_objet (0x804b038); _value(4); adresse_objet_suivant (0x804b048);
Adresse_objet (0x804b048); _value(5); adresse_objet_suivant (0x804b058);
Adresse_objet (0x804b058); _value(6); adresse_objet_suivant (0x804b068);
Adresse_objet (0x804b068); _value(7); adresse_objet_suivant (0);

Adresse_objet (0x804b008); _value(134524944); adresse_objet_suivant (0x804b018);
Adresse_objet (0x804b018); _value(134524960); adresse_objet_suivant (0x804b028);
Adresse_objet (0x804b028); _value(134524976); adresse_objet_suivant (0x804b038);
Adresse_objet (0x804b038); _value(134524992); adresse_objet_suivant (0x804b048);
Adresse_objet (0x804b048); _value(134525008); adresse_objet_suivant (0x804b058);
Adresse_objet (0x804b058); _value(134525024); adresse_objet_suivant (0x804b068);
Adresse_objet (0x804b068); _value(134525024); adresse_objet_suivant (0);
sizeof(liste) = 8

Normal quoi ... enfin je pense, d´après mes maigres connaissances des listes chainées.

Tout d´abords, je voulais savoir, si la manière de codée en tant que telle, était conventionnelle (je ne suis qu´un humble débutant), et si ma façon d´implémenter une liste chainée d´int l´est aussi. Merchi

De plus, lorsque je change l´ordre de définition des membres de la classe :

private :
unsigned int _value;
Liste* _next;

devient ->
private :
Liste* _next;
unsigned int _value;

J´ai une Segmentation fault ici ->
Adresse_objet (0x804b008); _value(1); adresse_objet_suivant (0x804b018);
Adresse_objet (0x804b018); _value(2); adresse_objet_suivant (0x804b028);
Adresse_objet (0x804b028); _value(3); adresse_objet_suivant (0x804b038);
Adresse_objet (0x804b038); _value(4); adresse_objet_suivant (0x804b048);
Adresse_objet (0x804b048); _value(5); adresse_objet_suivant (0x804b058);
Adresse_objet (0x804b058); _value(6); adresse_objet_suivant (0x804b068);
Adresse_objet (0x804b068); _value(7); adresse_objet_suivant (0);
Segmentation fault

Mais pourquoi ?
Merci bien d´avance et tout et tout .... :)

godrik
godrik
Niveau 30
29 mars 2006 à 17:44:33

si tu es débutant, ne t´encombre pas d´expression ternaire (? :) qui obscurcisse l´ecriture.

delete(liste);
cout << endl << endl;
liste->print();
c´est fatal que ca segfault ca.
tu détruis un objet et ensuite tu t´en sers...

gulius44
gulius44
Niveau 9
29 mars 2006 à 18:10:03

J´adoooore les expression tertiaire .... :) :) :)
Elle n´allourissent pas le code à mon sens (quoi que), mais sont je trouve, d´une grande utilité et très pratique dans certains cas .... dont celui là par exemple enfin à mon sens).

Bas oui, ça me parraissait bizzard que ça affiche encore quelque chose alors que je détruit l´objet liste (et les objets qui lui sont associés).

Mais pourquoi dans un cas ça marche - dans le cas
private :
unsigned int _value;
Liste* _next;

et pas dans l´autre :
private :
Liste* _next;
unsigned int _value;

?? ? Ca me parrait louche .... c´est encore un coup des russes ... :(

gulius44
gulius44
Niveau 9
29 mars 2006 à 18:22:14

J´ai changer le code .... en remplaçant le membre de classe :
unsigned int _value;
par unsigned int _value:3; // Valeur de 0 à 7, car
non-signé

Et alors qu´un sizeof(Liste) devrait être égale à 5 octets (4 pour le pointeur sur Liste, et 1 pour l´int de 3 bits) .... ça m´indique 8 octets .... :( pas glup du tout ....

Une partie du tuto de JY² sur le C/C++ parle de ce problème (par rapport aux structures, mais je suppose que c´est pareil pour les classes).
J´ai donc fait ce qu´il préconise, à savoir mettre les variables de moindre taille d´abords (c´est ça hein ?? si je ne me trompe).
Mais, ça ne marche pas .... :(
http://perso.numericable.fr/~fvirtman/info/tuto/G_13_memalign.cpp

Donc voili voilou

gulius44
gulius44
Niveau 9
29 mars 2006 à 18:24:38

Oups .... non c´est l´inverse .... déclarer les variables de plus grandes tailles d´abords, puis finir par les plus petites .....

dnob700
dnob700
Niveau 10
29 mars 2006 à 19:59:02

en vrac : si la classe fait 8 octets c´est "probablement" parce qu´un pointeur doit être aligné sur une adresse multiple de 4. Donc même si une instance pour tenir dans 5 octets, pour pouvoir en faire des tableaux, il faut qu´il rajoutte 3 octets (qui sont complétement perdus) après pour que l´objet suivant du tableau soit bien aligné aussi.

Pour l´opérateur ternaire, tu peut t´en servir, par contre utiliser un opérateur d´incrémentation (préfixe ou postfixe) dedans est une très très mauvaise idée, car même si a priori le test est effectué avant l´évaluation des expressions qu´il contient, je ne suis pas sûr que la norme l´impose et donc tu pourrait obtenir un résultat pas conforme à ce que tu attend (en règle général, ne serait-ce que pour la lisibilité du code, il ne faut jamais utiliser d´opération à effets de bords dans de grosses expressions où une même variables peut être accéder plusieurs fois).

gulius44
gulius44
Niveau 9
29 mars 2006 à 20:21:04

Pour la 2ème remarque, si je remplace par ça ??

_next = (*(t+1))?new Liste(++t):NULL;

Est-ce plus conventionnel ?? Ou du moins, moins crade ??

dnob700
dnob700
Niveau 10
29 mars 2006 à 20:42:03

non pareil, il vaut mieux le faire en deux étape.
sans compter que tes deux lignes ne sont pas du tout équivalente : dans l´un des cas t est toujours incrémenté, et dans l´autre seulement si le test réussie. Même si ça ne change rien là, ça montre pourquoi il est facile de faire des erreurs avec ça.

le mieux ici est d´utiliser deux fois t+1 ou alors de l´incrémenter une seule fois avant.

gulius44
gulius44
Niveau 9
29 mars 2006 à 21:04:08

Bien capitaine :merci: :merci:

gulius44
gulius44
Niveau 9
29 mars 2006 à 21:09:26

Et pour le fait que dans ce cas là :
private :
unsigned int _value;
Liste* _next;

Même quand on détruit l´objet, la méthode liste->print(); marche, ....

alors qu´avec cette déclaration de membres :
private :
Liste* _next;
unsigned int _value;

ça indique une Segmentation fault (ce qui semble logique, puisuqe le pointeur liste, ne point plus sur aucun objet .....

Fondamentalement, ça ne perturbera en rien le truc, mais je ne comprends pas pourquoi ça fait ça ..... ça me perturbe :)

Merci

dnob700
dnob700
Niveau 10
29 mars 2006 à 21:22:30

disons que ça on ne peut vraiment pas savoir, il faudrait vérifier que ce que tu dit est bien systématique (et que ça ne dépend pas parexemple de la zone en mémoire ou est chargé le programme (qui est générallement la même d´une exécution à l´autre quand tu le lance 5 fois de suite avec dev c++), etc.), et puis il faudrait regarder le code qui est généré pour voir si certaines opérations ne serait pas faites par exemple sur une copie de la variables (ou juste du membre en question) qui trainerait dans les registres depuis avant la destruction de l´objet, ou ce genre de chose

Et dans tout les cas, ceci est à la discretion du compilateur qui fait ce qu´il veut du moment que ça marche comme c´est cencé le faire (c´est-à-dire que l´on peut acceder à l´objet avant sa destruction, et après, rien n´empêche qu´on puisse toujours le faire).

gulius44
gulius44
Niveau 9
29 mars 2006 à 21:31:38

J´ai remplacé :
Liste::~Liste () {
delete(_next);
}

par

Liste::~Liste () {
delete(_next);
delete(this);
}

et ça marche niquel ....
Comprend pas des masses .... enfin j´ai des doutes, mais ça reste pas claire ....

C´est une RALNI (réaction à l´éxécution, non identifiée .... :( )

gulius44
gulius44
Niveau 9
29 mars 2006 à 21:45:21

Je pense savoir d´où ça vient, mais ... je n´en suis vraiment pas sur!!!

Il y a deux membres ds la classe Liste.
Un unsigned int servant à stocker la valeur de l´objet de la liste (c´est quand meme le but d´une liste chainée :) ), et un pointeur sur Liste.

Lorsque je fait, dans mon main(), delete(liste), ça appelle le destructeur de la première instance de la classe Liste (du première objet de la liste chainée).

Or, celui-ci, appelle le destructeur de l´objet suivant, qui lui même appelle le destructeur du suivant, ...., jusqu´au dernière objet, qui lui appelle un destructeur sur NULL.
AU final, rien n´aura été supprimé (à part peut-être la variable _value, qui n´est pas allouée dynamiquement).
Pour supprimer l´allocation du pointeur sur liste _next, il faut appeller delete(this).

?? ?

gulius44
gulius44
Niveau 9
29 mars 2006 à 22:01:16

A bas nan, j´ai parler trop vite ....

godrik
godrik
Niveau 30
30 mars 2006 à 09:54:05

non, delete this provoque un appel au destructeur puis libère la mémoire. le pointeur _next sera donc désaloué corretement.

Ce n´est pas parceque l´objet n´est plus alloué qu´il n´est pas fantome en mémoire. Tu utilises juste une zone mémoire qui est suceptible d´être modifiée (ce qui doit arriver a la fin).

gulius44
gulius44
Niveau 9
30 mars 2006 à 23:28:14

Okiiii doki!!!

Un autre problème (pfff j´en ai marre, pouqrquoi ça marche pas NORMALEMENT :( )

Adresse_objet (0x804b008); _value(1); adresse_objet_suivant (0x804b018);
Adresse_objet (0x804b018); _value(2); adresse_objet_suivant (0x804b028);

De 0x804b008 à 0x804b018, on a 16 octets, alors que la taille de l´objet (sizeof(Liste)), est censée être de 8 octets. Comprend pas quels sont ces 8 octets en plus ..... Delà viendrais peut-être les autres étrangetés .... ?

Merchi

dnob700
dnob700
Niveau 10
30 mars 2006 à 23:31:48

tes listes chainées sont alloué dynamiquement, donc pas forcément de manière continue dans la mémoire, car chaque appel à new peut te renvoyer une adresse quasiment n´importe où dans la mémoire.
Si tu veux que ce soit contigue, il faut que tu alloue directement un tableau avec tes éléments de liste chainée (ce qui ne t´empêche pas de les chainés quand même après).

gulius44
gulius44
Niveau 9
30 mars 2006 à 23:55:25

D´accords.... Mais c´est tout de même étrange que l´allocution de la mémoire, ce fait de manière régulière ....
De même, j´ai beau rééxécuter mon programme (même après un rebootage), ce sont toujours sur les même adresses mémoires, que sont définies les pointeurs sur objet Liste .... alors pour une allocation dynamique de la mémoire, je trouve ça bien .... statique ....

Après c´est surement un comportement qui me dépasse .... je n´ai que d´humbles connaissances sur ce sujet pourtant très très très passionant (la gestion de la mémoire) -> ça commence comme ça, et ça finis par faire de la programmation système en C/Assembleur :) :p)
Le pieds si jen arrive là un jour :)

godrik
godrik
Niveau 30
31 mars 2006 à 18:04:58

"De même, j´ai beau rééxécuter mon programme (même après un rebootage), ce sont toujours sur les même adresses mémoires, que sont définies les pointeurs sur objet Liste .... alors pour une allocation dynamique de la mémoire, je trouve ça bien .... statique"

C´est plutot normale. L´adressage de ton processus ne dépend QUE de ton processus et pas des autres processus qui tourne sur ta machine (les espaces memoire sont cloisonés). S´il se passe exactement les memes choses (meme entre du programme), alors la mémoire est structuré exactement de la meme manière.

Je donne une version simple de ce que fait new:
il dispose d´un gros bloc de mémoire issue du systeme. disons un void memoire[BEAUCOUP]; au début de l´execution de ton programme ce bloc de mémoire contient 1|BEAUCOUP-5|plein de case de valeur dont on s´en fout. La sémantique du 1 |BEAUCOUP-5 c´est "il y a BEAUCOUP-5 cases qui sont vides"
si tu alloue 4 octets la mémoire deviendra:
0|4|tadonnéde4octet|1|BEAUCOUP-10|plein de truc vide
ainsi tu sais ou il ya de la place. L´allocation est dynamqiue, mais n´est pas magique! :)

gulius44
gulius44
Niveau 9
01 avril 2006 à 12:14:44

Bas merci bien....
QUand j´utilise des pointeurs, ainsi que tout autre chose ayant rapport à la gestion de la mémoire (comme l´opérateur new), je sais pourquoi je les utilies, et ce qu´ils font (leur utilité).

Mais à vrai dire, mes connaissances sur la façon dont l´assamblage, l´allocution de la mémoire, ...., ce font .... ne sont pas très élevées.

J´en serais un peu plus....
D´où l´utilité de s´intérésser à des languages bas niveau (le C/C++ c´est une chose ... mais il ne fait pas tout comprendre), tel l´assembleur (ce que je viens tout juste de faire, et à vrai dire ça t´éclaire bcp, sur le focntionnement de la mémoire, ..., menfin ya bcp à apprendre encore .... :( :) )

Merci bien

Sous forums
  • Aide à l'achat Mac
  • Création de sites web
  • Internet
  • Macintosh
  • Création de Jeux
  • Linux
  • Programmation
  • Steam Deck
  • Hardware
La vidéo du moment