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 .... ![]()
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...
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 ... ![]()
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
Oups .... non c´est l´inverse .... déclarer les variables de plus grandes tailles d´abords, puis finir par les plus petites .....
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).
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 ??
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.
Bien capitaine
![]()
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
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).
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 ....
)
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).
?? ?
A bas nan, j´ai parler trop vite ....
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).
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
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).
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
![]()
Le pieds si jen arrive là un jour ![]()
"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! ![]()
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