Forum Programmation.c Effacer un pointeur fournit par une librairie externe

Posté par .
Tags : aucun
1
10
nov.
2010
Salut,

J'ai un code en C++ qui appelle une libraire C qui me retourne un tableau


while(...){
tmp tab =GetMachinTruc()
//DoSomething
}


où GetMachinTruc vient de la librairie C

dans le code de UINT16* GetMachinTruc()

tab=malloc()...
return tab


Donc j'ai donc mon tmptab qui est alloué dans GetMachinTruc
ce qui me donne sachant que je suis dans une loop une grosse fuite de mémoire.
Si je fait

while(...) {
UINT16* tmptab=GetMachinTruc
//Do Something with tmptab
free(tmptab)
}

J'ai droit a une superbe Segmentation fault pareil si j'utilise delete (car je le rapelle mon code est en C++ c'est la librairier qui est en C)

C'est là ou je me rend compte qu'en fait je suis à la masse sur la gestion de la mémoire, entre multiple fonction

-Est il autorisé d'éffacer un pointeur alloué ailleur ?
Si non pourquoi le compilateur n'a rien dit ?
-Y a t'il d'autre vérification à faire avant ?


Merci à tous

P.S.
//Do something correspond à copier le pointeur dans un container civilisé (un vecteur) qui lui même est propagé par référence dans tout le code.
  • # Plusieurs remarques

    Posté par . Évalué à 3.

    Déjà en C++ on aura plus tendance à utiliser "new" que "malloc". Enfin ça marche de toute façon, ça n'est pas le problème.


    Ensuite, si une zone mémoire est allouée avec "new", alors il faut la libérer avec "delete", si elle est allouée avec "new[]", on la libère avec "delete[]" et si elle est allouée comme en C avec malloc ou calloc ou les autres variantes, on la libère avec free.

    "Est il autorisé d'éffacer un pointeur alloué ailleur" n'a pas vraiment de sens en C ou en C++. On peut libérer un pointeur qui a été alloué par le processus courant. On ne peut pas libérer deux fois le même pointeur, ni libérer une adresse mémoire invalide

    Un petit exemple qui plante forcément :

    char * pointer = malloc(3);
    int i = 0;
    for (i = 0; i < 3; i++)
    *pointer++ = 0;

    free(pointer);

    --> on aura une erreur puisqu'on essaye de libérer une zone mémoire invalide.

    Sans en savoir plus, je pencherais sur un problème du type expliqué ci-dessus, ou alors tout simplement la zone mémoire libérée est utilisée ensuite dans un traitement ultérieur.

    • [^] # Re: Plusieurs remarques

      Posté par . Évalué à 1.

      Salut,
      Merci pour tes précisions
      -Oui je sais qu'en C++ c'est plutôt du new/delete Saut qu'en pratique je linke dynamiquement un code C avec un code C++ sans passer par un wrapper ou quelque chose de ce genre là.

      -Alors si la zone de mémoire libérée était utilisé plus tard j'imagine que j'aurais le segmentation fault plus tard est pas à l'appel de free (gdb m'indique clairement que le segmentation fault a lieu à l'appel de free)

      Sur le traitement que je fait subir à mon pointeur :


      198 UINT16* tmpseries=getChannelXData(ch_id+1); //Ici je fait appel à une libraire C dynamique
      199 for (int ii=0;ii<numberofsamples;++ii) {
      200 thistrace.PushBack(double(tmpseries[ii])); //(écriture dans un "vecteur à notre sauce")
      201 }
      202 if (tmpseries!=NULL) {
      203 free(tmpseries);
      204 tmpseries=NULL;
      205 }

      Sachant que si je ne delete pas tmpseries, les valeurs propagée dans la suite du programme ne sont pas abérantes, donc je ne pense pas qu'il y ai d'erreur à l'écriture ou à la lecture
      • [^] # Re: Plusieurs remarques

        Posté par . Évalué à 2.

        N'y aurait-il pas tout simplement un "freeChannelXData" dans la librairie, ou en tous cas un moyen prévu par la librairie pour libérer ses données ?


        Je n'en suis pas sûr du tout, mais le fait de libérer les données en dehors de la librairie pourrait être la cause du plantage si c'est une librairie dynamique.
        • [^] # Re: Plusieurs remarques

          Posté par . Évalué à 1.

          Non justement il y a pas de free...
          (Et la variable retournée est locale dans la fonction)
          J'en ai touché 2 mots à mon collègues, j'éspère que quelque chose va en sortir rapidement sinon je devrais m'en ocupper moi même.
      • [^] # Re: Plusieurs remarques

        Posté par . Évalué à 1.

        Et que dit la documentation de getChannelXData() à propos de la mémoire allouée pour le résultat ? Est-il indiqué que la libération est à la charge du code appelant ?

        Sinon, pour le code source reproduit ici, il n'est pas necessaire de tester le pointeur à NULL avant de faire le free(), ca ne sert a rien. Par contre, avant la boucle for, ca pourrait être plus utile...
      • [^] # Re: Plusieurs remarques

        Posté par (page perso) . Évalué à 2.

        Bonsoir inconnu,

        Malheureusement je n'ai pas la solution du problème. Juste quelques commentaires. Désolé.

        Il me semble évident que vous avez bien parfaitement modélisé le problème. Et pour autant que je sache votre free semble parfaitement valide. Si tant est que le modèle présenté est correct.

        Je ne vois donc que quelques commentaires à faire :
        - avez vous envisagé qu'en réalité les choses ne se passent pas exactement comme prévu. Par exemple le pointeur tmptab n'est pas vraiment alloué comme prévu, etc. Et dans ce cas il faut suivre au debugger ce qui se passe étape par étape pour soudain observer que le modèle n'est pas bon.
        - Une autre possibilité serait un bug du compilateur (par exemple lié au mélange de C et C++). Mais ce serait tellement étrange. À la fois faire le test ne coûte pas grand chose…
        - Quelque part, il me semble avoir lu que les mélanges de malloc/free et new/delete sont proscrits ; non seulement pour un tableau (ça vous l'avez parfaitement respecté) mais aussi dans un même code. Je ne suis pas certains que ce soit juste cependant. Le même bouquin prétendait qu'il était cependant possible moyennant quelques précautions de faire le mélange. Si votre problème n'est pas résolu d'ici lundi, je me ferais un plaisir de vous rapporter ce que disait ce livre.
        • [^] # Re: Plusieurs remarques

          Posté par . Évalué à 4.

          il me semble avoir lu que les mélanges de malloc/free et new/delete sont proscrits ; non seulement pour un tableau (ça vous l'avez parfaitement respecté) mais aussi dans un même code.

          Ce qui pose problème, c'est la libération par free() d'un pointeur alloué par new, et réciproquement delete pour malloc(). Mais il n'y a pas de contre indication technique a l'utilisation des deux systêmes dans le même code, sous réserver de respecter la régle précédente.
          Eventuellement, on pourra dire que le mélange des deux est une faute de style, une horreur pour la maintenance, une plaie pour la gestion des erreurs, etc. mais ca ne va pas plus loin.

          Autrement dit, si le bouquin en question affirme que le code suivant est proscrit pour raison technique :

          void foobar() {
          char* x = (char*)malloc(1) ;
          char* y = new(nothrow) char ;
          // ...
          delete y ;
          free(x);
          }


          alors mauvais bouquin, changer bouquin.
      • [^] # Re: Plusieurs remarques

        Posté par . Évalué à 1.

        Tiens, c'est marrant, tu vérifies que tmpseries n'est pas nul juste avant de le libérer alors que tu y as accédé 2 lignes plus haut. :D
    • [^] # Re: Plusieurs remarques

      Posté par (page perso) . Évalué à 3.

      Un petit exemple qui plante forcément :

      Il y a très peu de programmes qui "plantent" forcément. On a un comportement indéfini, qui peut ou non se traduire par un plantage. C'est important de le savoir, parce que ce n'est pas parce qu'un programme ne plante pas qu'il ne contient pas d'erreur de gestion de la mémoire.
      • [^] # Re: Plusieurs remarques

        Posté par . Évalué à 2.

        Exact, j'avais eu toutes les peines du monde il y a quelques années pour faire comprendre à des collègues que, quand un programme fonctionne en mode "Debug" et pas en mode "Release", le problème ne venait pas forcément du compilateur mais a bien plus de chances de venir de son code.

        En particulier, accéder un pointeur non défini peut très bien fonctionner en fonction de "où on tombe", d'où des bugs détectés parfois très tard.


        Pour mon exemple, il me semble que dans ce cas précis, la libc détecte immédiatement l'erreur, mais il est vrai que dans parfois, c'est bien plus tard que la corruption est détectée, et c'est alors difficile de remonter à la vraie erreur.
        • [^] # Re: Plusieurs remarques

          Posté par . Évalué à 4.

          valgrind !!!!(oui bon avec un projet ayant du versant ou ilog c'est très verbeux)
          Depuis que j'ai eu accès à ce truc les fuites mémoires sont un lointain souvenir

          Alfonse Zeymer

          Il ne faut pas décorner les boeufs avant d'avoir semé le vent

  • # free ... ou pas ?

    Posté par . Évalué à 3.

    Est il autorisé d'éffacer un pointeur alloué ailleur ?

    c'est autorisé, mais déconseillé.
    La règle usuelle : c'est celui qui alloue qui désalloue

    Confier l'allocation et la désallocation à des layers différents du code complique énormément la maintenance (et donc favorise les bugs).

    Là, c'est à la lib de fournir les APIs qu'il faut pour (au choix):
    - désallouer la mémoire allouée,
    - laisser la charge de l'allocation/désallocation à l'appelant

    //Do something correspond à copier le pointeur dans un container civilisé (un vecteur) qui lui même est propagé par référence dans tout le code.

    possible que j'interprète mal, mais si tu copie des *pointeurs* dans ton vecteur, et que tu désalloues la mémoire pointée, forcément ...
    • [^] # Re: free ... ou pas ?

      Posté par . Évalué à 1.

      possible que j'interprète mal, mais si tu copie des *pointeurs* dans ton vecteur, et que tu désalloues la mémoire pointée, forcément ...

      Tu interprète mal, regardes les ligne 199-200 dans le codé posté ci-dessus : le pointeur en question est en fait un tableau, et il y a copie du contenu de ce tableau dans un vecteur via une boucle for. Donc le pointeur lui même n'est pas mémorisé, il n'est plus utilisé après la boucle, on peut le libérer.

      Pour le reste, 100% d'accord.
  • # déjà

    Posté par (page perso) . Évalué à 3.

    Déjà, les bugs de code, ça demande le code.
    Donc tu files ton code, ta lib, et à partir de là, on regarde.

    Sinon, une question :

    int* tab=allocmachin();
    tab++;
    free(tab)

    ne risque-t-il pas de causer un bug car tu fais un free au pif, et non au point de départ de tes données allouées ?

    Je ne sais pas comment malloc gère la mémoire, mais je pense qu'il n'aime pas (lui, ou l'OS) qu'on lui demande de désallouer des données qui ne sont pas connues (car j'imagine qu'il tient à jour une table de (adresse-initiale, taille-allouée))

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.