Probleme étrange avec realloc ...

Probleme étrange avec realloc ... - C - Programmation

Marsh Posté le 23-03-2006 à 17:14:20    

Bonjour à tous,
 
Je m'adresse à vous car j'ai un étrange problème avec realloc. Apparemment c'est lui le fautif  :lol:.
 
Je m'explique voici mon code :
 

Code :
  1. typedef struct {
  2. ....
  3. }montype;
  4. typedef struct tab{
  5.     montype* element;
  6.     int taille;
  7. }tab;
  8. void ajouterE(tab t){
  9.     element e;
  10.     initialiserElement(&e);
  11.     t.taille += 1;
  12.     montype* tmp;
  13.     tmp = (montype*)realloc(t.element,t.taille* sizeof(montype));
  14.     if(tmp == NULL){
  15.        exit(5);
  16.     }
  17.     else{
  18.        t.element = tmp;
  19.        t.element[t.taille-1] = e;
  20.     }
  21. }


 
L'affectation en rouge fait tout planter ... (sgmentation fault) alors que l'instruction t.element[t.taille-1]; passe lors de l'execution. Est ce que quelqu'un sait pourquoi?  :jap:  
 
Merci d'avance!

Reply

Marsh Posté le 23-03-2006 à 17:14:20   

Reply

Marsh Posté le 23-03-2006 à 17:19:21    

Au lieu de realloc(), il paraitrait plus logique de faire appel à malloc(), car il semble que tmp n'a jamais été alloué avant. Cela dit, je ne sais pas si c'est ça qui cause le problème.

Reply

Marsh Posté le 23-03-2006 à 17:21:55    

Que fait la fonction initialiserElement() ?
Normal que tu enregistres un "element" dans une variable de type "montype", ou c'est juste une erreur de copier/coller ?
 
 

olivthill a écrit :

Au lieu de realloc(), il paraitrait plus logique de faire appel à malloc(), car il semble que tmp n'a jamais été alloué avant. Cela dit, je ne sais pas si c'est ça qui cause le problème.


 
"tmp" n'est pas utilisé parce que c'est la variable temporaire pour recevoir le résultat de realloc().
C'est la structure "t" qui est importante ici, et elle contient peut-être déjà des choses, surtout vu le nom de la fonction. ;)


Message édité par Elmoricq le 23-03-2006 à 17:23:14
Reply

Marsh Posté le 23-03-2006 à 17:28:21    

Oui, tu as raison Elmoricq, ma remarque n'est pas judicieuse car, en effet, on a le droit de faire ce genre de réallocation, même si jusqu'à présent, je n'avais vu que des realloc sur soi-même.

Reply

Marsh Posté le 23-03-2006 à 17:40:22    

C'est une réallocation sur "soi-même".  
 
La fonction s'applique bien sur t.element.
Traditionnellement, on assigne le résultat de realloc() à une variable temporaire, car si realloc() échoue et retourne NULL, la variable initiale (ici, t.element) est toujours valide.
L'utilisation d'une variable temporaire permet donc de continuer le programme sans perdre l'adresse du pointeur d'origine (même si ici l'intérêt est limité puisqu'un échec de realloc() est suivi d'une sortie du programme).

Reply

Marsh Posté le 23-03-2006 à 18:05:40    

faudrais plutot passer l'adresse de tab à la fonction (la tu modifie une copie locale)

Reply

Marsh Posté le 23-03-2006 à 19:01:43    

rital_5_4 a écrit :

Code :
  1. typedef struct {
  2. ....
  3. }montype;
  4. typedef struct tab{
  5.     montype* element;
  6.     int taille;
  7. }tab;
  8. void ajouterE(tab t){
  9.     element e;
  10.     initialiserElement(&e);
  11.     t.taille += 1;
  12.     montype* tmp;
  13.     tmp = (montype*)realloc(t.element,t.taille* sizeof(montype));
  14.     if(tmp == NULL){
  15.        exit(5);
  16.     }
  17.     else{
  18.        t.element = tmp;
  19.        t.element[t.taille-1] = e;
  20.     }
  21. }




 
1) ta fonction "ajouterE" reçoit une copie de ta variable que tu lui passes. Et copier une structure n'est pas du tout une bonne idée. Surtout que avec ton realloc tu ne modifies que le pointeur copié et non le pointeur original.
 
2) ta variable "e" est une variable de type "element" or, dans cette partie de code, le type "element" n'existe pas. Peut-être que tu voulais écrire "montype e"...
De toute façon la variable "e" est locale donc elle sera définitivement perdue lorsque tu quitteras la fonction. Or tu la copies dans t.element[...]. Si c'est un type simple, ça va mais si "e" est structurellement plus complexe, tu risques de ne copier que des ombres. A la limite en mettant "e" en static tu peux t'en sortir mais d'une façon générale, il y a quand-même un pb de conception d'où ton erreur sur le realloc qui est caractéristique d'une fuite de mémoire
 

olivthill a écrit :

Au lieu de realloc(), il paraitrait plus logique de faire appel à malloc(), car il semble que tmp n'a jamais été alloué avant. Cela dit, je ne sais pas si c'est ça qui cause le problème.


On a tout a fait le droit de faire du realloc même sur une variable qui n'a jamais été allouée mais faut qu'elle soit initialisée à "NULL" (le realloc fait alors un simple malloc). Cela permet de faire des boucles de realloc sans être obligé d'écrire le malloc initial...


Message édité par Sve@r le 23-03-2006 à 19:07:16

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 23-03-2006 à 19:51:14    

Citation :

ta variable "e" est une variable de type "element" or, dans cette partie de code, le type "element" n'existe pas. Peut-être que tu voulais écrire "montype e"...


 
Effectivement il y a une faute de frappe la variable e est de type montype en réalité veuillez m'exuser  :jap: !
 

Citation :

Et copier une structure n'est pas du tout une bonne idée. Surtout que avec ton realloc tu ne modifies que le pointeur copié et non le pointeur original


 
Je n'ai pas vraiment le choix ici ... désolé je ne suis pas un pro en c ^^ ... y a t-il une autre solution plus propre?
 

Citation :


De toute façon la variable "e" est locale donc elle sera définitivement perdue lorsque tu quitteras la fonction. Or tu la copies dans t.element[...]. Si c'est un type simple, ça va mais si "e" est structurellement plus complexe, tu risques de ne copier que des ombres.

.
Ouais en fait e contient elle même un tableau 2D et des attributs entiers ect ... et c'est meme pire que de l'ombre ca fait tout planter...  
 

Citation :

A la limite en mettant "e" en static tu peux t'en sortir mais d'une façon générale, il y a quand-même un pb de conception d'où ton erreur sur le realloc qui est caractéristique d'une fuite de mémoire


 
Le coup du static pourquoi pas je peux essayer si ca marche mais c'est sure que si je peux éviter :).
 

Citation :

Que fait la fonction initialiserElement() ?


 
Pas grand chose elle se contente d'initialiser la taille du tableau à 0.
 

Citation :

faudrais plutot passer l'adresse de tab à la fonction (la tu modifie une copie locale)


 
Je peux essayer effectivement vu que le tableau est déclaré dans le main. Mais qu'entend tu par modifier une copie locale ?


Message édité par rital_5_4 le 23-03-2006 à 19:55:10
Reply

Marsh Posté le 23-03-2006 à 20:17:02    

les parametres sont passé par valeur en C
 
le prototype doit plutot etre  
void ajouterE(const montype * e);

Reply

Marsh Posté le 23-03-2006 à 20:17:51    

C'est a n'y rien comprendre la déclaration tab montableau; provoque une erreur de segmentation .... je n'y comprend plus rien.
 
 
Sinon pour le static c'est rapé je viens de tester.
 
Voici le nouveau code qui n'a pas l'air de marcher beaucoup mieux avec le passage de tableau par adresse :

Code :
  1. void ajouterE(tab* t,montype e){
  2.     (*t).taille += 1;
  3.     montype* tmp;
  4.     tmp = (montype*)realloc((*t).element,(*t).taille* sizeof(montype));
  5.     if(tmp == NULL){
  6.        exit(5);
  7.     }
  8.     else{
  9.        (*t).element = tmp;
  10.        (*t).element[(*t).taille-1] = e;
  11.     }
  12. }


 
OU ALORS procéder comme ci-dessous :
 
Sinon est-il possible de passer l'élément de type montype par adresse et de faire une affectation du genre (*t).element[(*t).taille-1] = (*r); enfin dans cet esprit...
En disant ca je pense a passer e en adresse et de recopier les champs 1 à 1 comme ceci par exemple
 
(*t).element[(*t).taille-1].x = (*e).x;

Message cité 1 fois
Message édité par rital_5_4 le 23-03-2006 à 20:23:06
Reply

Marsh Posté le 23-03-2006 à 20:17:51   

Reply

Marsh Posté le 23-03-2006 à 20:24:38    

Citation :

Voici le nouveau code qui n'a pas l'air de marcher beaucoup mieux avec le passage de tableau par adresse


passage de l'adresse du tableau [:aloy]
 
tu peux remplacer (*t). par t->
 

Citation :

Sinon est-il possible de passer l'élément de type montype par adresse et de faire une affectation du genre (*t).element[(*t).taille-1] = (*r); enfin dans cet esprit...


 
oui
 

Citation :

void ajouterE(tab* t,const montype * e){...}

Reply

Marsh Posté le 23-03-2006 à 20:25:41    

Citation :

En disant ca je pense a passer e en adresse et de recopier les champs 1 à 1 comme ceci par exemple
 
(*t).element[(*t).taille-1].x = (*e).x;


 
affectation ou memcpy suffit

Reply

Marsh Posté le 23-03-2006 à 23:18:02    

Citation :

tu peux remplacer (*t). par t->


 
Quelle est la différence, j'ai essayé le cout du const ca n'a pas l'air de marcher mais par contre ce qui est bizar c'est que l'instruction tab tableau; donne lieu à un segmentation  fault sauf a la premiere execution. Je pense que la mémoire doit être corrompu dans le tas à un endroit et que après les executions suivantes plantent. Qu'en pensez vous?
 
Ca m'embete vraiment cette histoire car j'ai besoin de cette fonction  :cry:


Message édité par rital_5_4 le 23-03-2006 à 23:18:40
Reply

Marsh Posté le 23-03-2006 à 23:43:27    

Je reposte tout le code au cas ou :

Code :
  1. void initTableau(tab* t){
  2.    (*t).taille = 0;
  3. }
  4. void ajouterE(tab* t,const montype* e){
  5.     (*t).taille += 1; // On augmente la taille
  6.     montype* tmp;
  7.     tmp = (montype*)realloc((*t).element,(*t).taille* sizeof(montype));
  8.     if(tmp == NULL){
  9.        exit(5);
  10.     }
  11.     else{
  12.        (*t).element = tmp;
  13.        (*t).element[(*t).taille-1] = (*e);
  14.     }
  15. }
  16. int void main(){
  17. ...
  18. tab tab2; // Plantage parfois lors le l'execution
  19. initTableauR(&tab);
  20. ...
  21. montype e;
  22. ajouterE(&tab2,&e); // Plantage total ....
  23. }

Reply

Marsh Posté le 23-03-2006 à 23:55:59    

Peut-être qu'en montrant encore un peu plus de code, cela aiderait.

Message cité 1 fois
Message édité par olivthill le 23-03-2006 à 23:58:10
Reply

Marsh Posté le 24-03-2006 à 00:03:25    

Citation :

Quelle est la différence


 
c'est la meme chose, mais la syntaxe "->" est quand meme moin lourde
 

Citation :

Code :
  1. tmp = (montype*)realloc((*t).element,(*t).taille* sizeof(montype));



c'est quand meme plus claire comme ca

Code :
  1. tmp = realloc(t->element, t->taille * sizeof *tmp);


Reply

Marsh Posté le 24-03-2006 à 00:14:10    

olivthill a écrit :

Peut-être qu'en montrant encore un peu plus de code, cela aiderait.


 
Et bien a priori pour chaque nouvelle élément j'ai une fonction qui l'initialise :  
 

Code :
  1. void initElement(montype* e){
  2.      (*e).u = a;
  3.      (*e).v = b;
  4.      (*e).t = z;
  5.      .....
  6.      (*e).tableau= f1();; // Avec f1 qui retourne un tableau ayant été alloué dynamiquement au préalable  
  7. }
  8. int main(){
  9. ....
  10. montype e;
  11. initElement(&e);
  12. }


 
 
Je precise que quand je fait des trucs du genre tout marche parfaitement ...

Citation :


int main(){
....
montype e,e1;
initElement(&e);
initElement(&e1);
 
funct(&e) ....
}


Peut être manque t-il quelque chose effectivement ...  :pt1cable:


Message édité par rital_5_4 le 24-03-2006 à 00:19:05
Reply

Marsh Posté le 24-03-2006 à 00:18:45    

montre en plus

Reply

Marsh Posté le 24-03-2006 à 00:24:42    

Code :
  1. typedef struct {
  2.   int toto;
  3.   int tutu;
  4. }montype2;
  5. montype2** f1(){
  6.        allocation dynamique     ..........
  7. }


Message édité par rital_5_4 le 24-03-2006 à 00:25:23
Reply

Marsh Posté le 24-03-2006 à 00:30:35    

pourquoi le type retour est montype2** ?
 
essaies de montrer du vrai code (celui dont tu doutes, genre les allocations), avec ca c'est difficile de t'aider

Reply

Marsh Posté le 24-03-2006 à 00:41:21    

skelter a écrit :

pourquoi le type retour est montype2** ?
 
essaies de montrer du vrai code (celui dont tu doutes, genre les allocations), avec ca c'est difficile de t'aider


 
Parce que la structure montype contient elle meme un tableau de type montype2 (montype.tableau = tableau 2d de type montype2) je sais que c'est le bordel ^^ sinon pour le code des allocations ca donne ca :
 

Code :
  1. montype2** f1(int x,int y){
  2.   montype2** s;
  3.   //allocation dynamique
  4.   s = (montype2**)malloc(sizeof(montype2*)*y);
  5.   int i,j;
  6.   for(i=0; i < hauteur; i++){
  7.     s[i] = (montype2*)malloc(sizeof(montype2)*x);
  8.   }
  9.   // initialisation des elements
  10.   for(i=0; i < x; i++){
  11.     for(j=0; j < y; j++){
  12.       s[i][j].toto= 0;
  13.     }
  14.   }
  15.   return s;
  16. }


Message édité par rital_5_4 le 24-03-2006 à 00:43:27
Reply

Marsh Posté le 24-03-2006 à 00:49:20    

#
for(i=0; i < x; i++){
#
   for(j=0; j < y; j++){
-->
#
for(i=0; i < y; i++){
#
   for(j=0; j < x; j++){
 
t'as inversé x et y

Reply

Marsh Posté le 24-03-2006 à 01:12:49    

rital_5_4 a écrit :

C'est a n'y rien comprendre la déclaration tab montableau; provoque une erreur de segmentation .... je n'y comprend plus rien.


 
Bon, on arrête la souffrance. J'ai l'impression que tu veux créer une fonction qui ajoute un certain type d'élément à la fin d'un tableau non ???
 
Bon, on y va (sur un air de Raphael, ça va m'aider à taper sereinement...) - Je te fais un exemple à l'image de ton premier post...

Code :
  1. // Définition de la structure permettant de gérer un type complexe
  2. typedef struct {
  3.     int x;
  4.     int y;
  5.     int z;
  6.     ... // Tout ce que tu veux
  7. } t_montype;
  8. // Définition de la structure permettant de gérer un tableau de types complexes
  9. typedef struct {
  10.    t_montype *listeElement;
  11.    size_t nbElement;
  12.    size_t taille;
  13. } t_tab;
  14. // Fonction permettant d'initialiser une "t_montype"
  15. void initialiserElement (t_montype *pt)
  16. {
  17.     pt->x=...;
  18.     pt->y=...;
  19.     pt->z=...;
  20. }
  21. // Fonction permettant d'ajouter un élément dans mon tableau
  22. int AjouterElement(t_tab *tab)
  23. {
  24.      t_montype elem;
  25.      // Initialisation de l'élément
  26.      initialiserElement(&elem);
  27.      // Si le nb d'élément déjà présents a atteint la taille allouée => le nouveau ne tiendra pas
  28.      if (tab->nb_elem == taille)
  29.      {
  30.           // On agrandit la taille (de 100 par exemple)
  31.           tab->taille+=100;
  32.           // on réalloue un tableau plus grand
  33.           tab->listeElement=realloc(tab->listeElement, taille * sizeof(t_element));
  34.           // Réallocation échouée => fin fonction
  35.           if (tab->listeElement == NULL)
  36.              return(-1);
  37.       }
  38.       // On copie l'élément initialisé à son emplacement
  39.       memcpy(&tab->listeElement[nb_elem], &elem, sizeof(t_element));
  40.       // On incrémente le nb d'éléments
  41.       tab->nb_elem+=1;
  42.       // on renvoie une valeur pouvant signifier "ok" (ce qu'on veut sauf "-1" )
  43.       return(tab->taille);
  44. }
  45. // Fonction principale (qui est "int", pas "int void" !!!)
  46. int main()
  47. {
  48.      // Création de mon tableau
  49.      t_tab tab;
  50.      int i;
  51.      // Initialisation tab (obligatoire)
  52.      tab.listeElement=NULL;
  53.      tab.nb_elem=0;
  54.      tab.taille=0;
  55.      // Remplissage de "n" éléments
  56.      for (i=0; i < ...; i++)
  57.      {
  58.            if (ajouterElement(&tab) == (-1))
  59.            {
  60.               printf("Echec ajout i=%d\n", i);
  61.               exit(-1);
  62.            }
  63.      }
  64.      // Ici, mon tableau est rempli
  65.      ....
  66. }


     
La principale différence avec ton premier programme, c'est que je passe par "memcpy" pour copier ma variable "e". "memcpy" copie tous les octets de "e" dans la zone destinatrice. Si "e" ne contient pas de pointeur, c'est bon. "memcpy" est la fonction à utiliser quand on doit copier une variable complexe dans une autre à condition que la variable ne contienne pas de pointeur. Si c'est le cas, alors c'est à toi de te créer la fonction de copie spéciale qui ira allouer dans la zone réceptrice les pointeurs et y copier ceux de la zone émettrice.
 
Quelques petites différences avec ton pgm
- je passe par une variable "taille" me donnant la taille allouée. Cela me permet de ne pas appeler "realloc" (gourmand) à chaque ajout... mais m'oblige à allouer des blocs de 100 "t_element". L'avantage de cette méthode est que tu peux paramétrer facilement le nb de blocs (en le mettant à "1", cela revient à faire comme t'avais fait)
- nommer ses types "t_qqchose" est assez recommandé - permet d'éviter plus tard de les confondre avec des variables.
- je ne passe pas par un pointeur intermédiaire pour realloc. Etant donné que si realloc échoue, cela ne m'intéresse pas de garder ma liste puisque je ne peux pas m'en servir. Mais rien ne t'interdit d'en utiliser un si t'en as envie
- ma fonction "ajouter" renvoie un code spécial quand elle échoue... et c'est la fonction appelante qui gère en fonction du code reçu. Ne jamais faire de "exit" brutaux dans une sous-fonction => horrible
- j'espère que tu remarques l'utilité des commentaires...
 


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 24-03-2006 à 09:00:38    

Citation :

"memcpy" est la fonction à utiliser quand on doit copier une variable complexe dans une autre à condition que la variable ne contienne pas de pointeur


 
oui, mais la le type est une structure donc on pouvait aussi bien utilisé l'opérateur =, mais dans son cas la solution il faudrais effectivement que l'appelant (pour etre générique) fournisse la fonction de copie
 

Citation :

Cela me permet de ne pas appeler "realloc" (gourmand) à chaque ajout... mais m'oblige à allouer des blocs de 100 "t_element"


 
realloc n'implique pas forcement un deplacement de la zone allouée et faire grossir exponentielement la zone allouée ca peut etre encore plus efficace
 

Citation :

nommer ses types "t_qqchose" est assez recommandé - permet d'éviter plus tard de les confondre avec des variables.


 
ouai, ou alors faire comme de nombreuses lib, utilisés un préfixe (pour protèger ses noms) pour chaque types de namespaces (macros -> tout en maj, fonctions -> tout en min, types -> premiere lettre en maj)
 

Citation :

- je ne passe pas par un pointeur intermédiaire pour realloc. Etant donné que si realloc échoue, cela ne m'intéresse pas de garder ma liste puisque je ne peux pas m'en servir.


 
dans ce cas ca fuit, tu perds la valeur du pointeur
 
sinon, accessoirement faudrais penser à faire quelques free quand c'est nécéssaire :)

Reply

Marsh Posté le 24-03-2006 à 12:58:26    

Bonjour à tous,
 
J'ai enfin réussi à faire marcher cette fonction et le plus étrange c'est que je n'ai rien changé par rapport à hier. Je pense que mes premiers essai avaient du corrompre certaines données dans le tas suite à une fuite mémoire et qu'en redemarrant le pc ce matin ca a mieux marché enfin je ne vois que ca...
 
Le code :

Code :
  1. void ajouterE(tab* t,const montype* e){
  2.     (*t).taille += 1;
  3.     montype* tmp;
  4.     tmp = (montype*)realloc((*t).element,(*t).taille* sizeof(montype));
  5.     if(tmp == NULL){
  6.        exit(5);
  7.     }
  8.     else{
  9.        (*t).element = tmp;
  10.        (*t).element[(*t).taille-1] = (*e);
  11.     }
  12. }


 
Toutefois je vais tenir compte de toutes les remarques afin d'améliorer mon code :).
 

Citation :

sinon, accessoirement faudrais penser à faire quelques free quand c'est nécéssaire :)


 
Les free sont fait pour chaque element du tableau en fin d'execution :).
 
Encore merci à tous  :jap: !

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed