Forum |  HardWare.fr | News | Articles | PC | S'identifier | S'inscrire | Shop Recherche
1320 connectés 

 


 Mot :   Pseudo :  
 
 Page :   1  2
Page Suivante
Auteur Sujet :

aide sur le realloc

n°1278277
Pheattarak
Posté le 06-01-2006 à 16:08:43  profilanswer
 

Reprise du message précédent :
Bonjour bossgama  :pt1cable:,
 
> Hehe, vous aimez écrire, vous. :D
 
     Ben oui et non. Disons que c'est mon gros défaut... je ne sais pas être si concis que ça et le pire, c'est que je déteste être mal compris. Du coup, mes phrases se font à rallonge. Désolé, je vais essayer de faire plus court.
 
     Avant de continuer, Emannuel a écrit beaucoup de chose et a répondu à beaucoup de vos questions donc je vais juste répondre aux questions que vous me posiez suite à mon dernier post : ce sera plus clair entre nous.. enfin, j'espère.
 
>Plus sérieusement, je ne comprends pas bien votre explication
>
>>Autrement dit, dès maintenant, prenez l'habitude lorsque vous avez un pointeur de le mettre de suite à NULL, de même que dès que vous avez une chaîne de caractère, d'écrire *c = '\0' : mettre un '\0' en premier caractère de la chaîne
 
     C'est une règle en programmation : "après la déclaration d'une variable, il faut penser à l'initialiser au plus tôt". On peut dire aussi "il faut éviter les déclarations et les remplacer par des définitions".
     La différence entre déclaration et une définition c'est que dans le cas de la déclaration on dit "cette variable est comme ça" (autrement dit, on dit, cette variable est de ce type). Le problème c'est que la variable, a priori en C, n'est pas prête à l'emploi.
     Et donc, si l'on ne fait que déclarer une variable et que l'on s'en sert par la suite sans lui donner une valeur "initiale" juste, le programme comportera par définition une erreur même s'il fonctionne bien. Je vais encore être obligé de faire long mais bon, allons-y pour un exemple.
     Il arrive souvent que l'on déclare une variable et que la définition arrive naturellement par la suite :
 

Code :
  1. int i;
  2. ...
  3. for(i = 0; i < n; i++) ... ;


 
     On trouvera idiot ici, d'écrire < int i = 0; > qui est une définition (puisque i prend une valeur, dite initiale ici). Cependant, ce genre d'oubli peuvent produire beaucoup de petits "bugs" lorsque l'on y fait pas attention car entre la déclaration de la variable (donc sans que cette variable n'aît de valeur "correcte" ) jusqu'à son utilisation, il est possible que l'on oublie parfois de donner une valeur correcte à la variable en question.
 
     Là, où on se dit "mais chuis pas bête quand-même" (pour être poli), il arrive que dans le cas des pointeurs, cette erreur de ne faire que des déclarations au lieux de définitions, soit carrément dangereux. Car voilà le problème des pointeurs vis-à-vis des autres variables : "on ne peut jamais, a priori, connaître l'utilisation du pointeur". C'est-à-dire qu'à un moment donné, on ne peut jamais être certains de ce que l'on veut ou va faire avec un pointeur. Dans l'absolu, c'est vrai aussi pour les autres variables (non-pointeurs) !.
     Du coup, un pointeur étant un variable d'un type dit "pointeur sur... un type", on sait qu'il peut avoir deux valeurs particulières : une valeur NULL pour dire "je ne pointe sur rien" (par exemple) ou une valeur "numérique" autre que NULL et alors ça veut dire "je pointe sur quelque chose" (quelque chose de valide on l'espère !).
 
     Mais voilà ! si l'on ne fait que déclarer un pointeur (sans l'initialiser donc), on est amener à écrire :
 

Code :
  1. int * p;
  2. ...
  3. ... /* Beaucoup beaucoup de code... */
  4. ...
  5. if (p != NULL) /*alors faire un truc joli...*/ ;


 
     On voit bien ici, que p n'a pas été initialisé du tout donc il peut contenir la valeur NULL (par défaut) ou une valeur numérique autre que NULL. Donc il peut contenir n'importe quoi après sa déclaration : et c'est bien le problème !
     Du coup, lorsque l'on arrive en ligne 5 du code et que l'on teste p avec NULL, on a tout faux ! puisque p n'a jamais été initialiser et donc quelque soit sa valeur à ce moment-là du code, le test ne signifie rien ! puisque p n'a aucune valeur qui soit représentatif de quoi que ce soit.
 
     Le problème majeur qui arrive par la suite c'est que beaucoup de compilateur intialise toutes les variables (au moins globale) à zéro et les pointeurs à NULL. Et là, vous aurez une erreur que vous aurez extrêmement de mal à trouver car sur votre compilateur et votre système tout marchera. Puis lorsque vous passerez votre code sur les machines de la NASA, la navette elle scratches !!!   :fou:
 
     Donc en résumé, même si ça coupe un peu de temps machine et d'octets... il faut préférer, au moins par prudence, ne faire que des définitions : c'est-à-dire des déclarations suivie d'une initialisation par défaut.
 
     Pfff, c'était long ah là là et c'était qu'une question  :??:. Bon la suite  :hello:
 
 
> Vous vous appercevez d'une chose assez "rigolote"... c'est que votre fonction reallocation n'a aucun intérêt ! puisqu'elle s'appelle "realloc()" déjà en C. Et le fait de l'avoir encapsulé dans une fonction n'y change rien car en sortie de fonction, vous êtes obligé de nouveau de tester si votre pointeur c est différent de celui que vous aviez auparavant.
 
     Vous avez tout à fait répondu vous-même... je disais justement que vous auriez pu utiliser realloc() au lieu de votre fonction réallocation(). Mais je voulais surtout insister sur le fait que dans le futur vous verrez, vous aller écrire tout plein de fonction et la majeure partie du temps, ces fonctions n'auront aucun intérêt sauf que d'allourdir votre code... c'est malheureux mais ça nous arrive tous je pense. En cas, moi, j'en étais le champion dans une autre vie hahaha.
 
     Mais pour vous dire exactement pourquoi votre fonction réallocation() n'apportait rien, c'est tout simplement parce qu'elle ne fournit aucun résultat directement testable... je m'explique.
     Vous avez un pointeur qui pointe sur une zone mémoire déjà alloué par malloc() (ou autre) et vous voulez agrandir la zone mémoire sur laquelle ce pointeur pointe. OK. Vous décidez de faire une fonction et c'est très juste même très utile. Mais votre fonction va faire quoi ? une réallocation effective... non ! c'est realloc() qui le fait. Alors que peut bien faire votre fonction de plus ?
     La réponse c'est vous qui l'avez, c'est votre choix. Prenons un exemple, moi, je fais le choix de me dire que la fonction reallocation() que j'écris va "m'empêcher" d'avoir à vérifier si une réallocation s'est effectivement bien passé ou pas.
     Ca peut sembler curieux de se demander si une réallocation a réussit ou non puisque de toute manière, ça ne change a priori rien sauf que si... Si j'ai un pointeur qui pointe sur une zone de 400 char en mémoire et que je veux 500 char, que j'utilise realloc(), comment puis-je savoir après la réallocation de combien de caractères je dispose ? 400 ou 500 ?
     Car en C, on ne peut pas (a priori) connaître la taille d'une zone mémoire qui nous est alloué. Sauf, si juste après la réallocation (dans ce cas), je teste le nouveau pointeur obtenu et que je compare son adresse à NULL. Qu'on me corrige si je me trompe mais il me semble que si j'ai NULL en retour d'une fonction realloc() ça veut dire que la réallocation n'a pas eu lieu.
     Donc dans mon exemple, si mon nouveau pointeur est NULL, je dispose toujours que de 400 char et si ça a réussit, je dispose de 500 char. Et moi, étant fénéant et souffrant de perte de mémoire chronique, je vais écrire quelque chose de plus explicite dans mon code en utilisant une fonction toute faite maison plutôt que d'utiliser realloc() et un test avec NULL par la suite... je vais donc par exemple écrire :
 

Code :
  1. bool reallocation_reussie( char ** s, int nouvelle_taille )
  2. {
  3.   char * tmp_realloc;
  4.   if (NULL != (tmp_realloc = realloc(s, nouvelle_taille * sizeof(char))) )
  5.   {
  6.     /* Réallocation réussie... je stocke la nouvelle adresse et je suis content */
  7.     *s = tmp_realloc;
  8.     return TRUE/* Réallocation avec succès */
  9.   }
  10.   else
  11.   {
  12.     /* Dans le cas contraire, ma réallocation a échoué... */
  13.     /* J'en informe le programme appelant de l'échec      */
  14.     return FALSE;
  15.   }
  16. }


 
     Bien entendu, j'aurai crée mon type bool avec les valeurs TRUE et FALSE pour vraie et faux. Cette fonction bien que ne faisant pas plus que realloc() a l'avantage néanmoins d'être plus claire dans son utilisation :
 

Code :
  1. int main(...)
  2. {
  3.   char * ma_chaine = NULL;
  4.   ...
  5.   if (reallocation_reussie(ma_chaine, 1024))
  6.   {
  7.      /* La réallocation est réussie, j'ai les ressources nécessaire pour stocker le nom de ma fusée... */
  8.      /* Passer à la phase 2 du protocole de décolage de la navette...                                         */
  9.      strcpy(ma_chaine, "Pourvu qu'elle n'explose pas !" );
  10.   }
  11. }


 
     Juste une remarque dans votre code. J'ai cru voir à un moment que vous allouiez (taille + 1) caractères pour votre chaîne de caractère. Ceci n'est pas très sage. Vous ne pouvez pas le savoir a priori bien sûr, mais en ce qui concerne les chaînes de caractères en C, il existe des usages. Un des usages les plus importants étant : "lorsque l'on veut n caractère pour sa chaîne de caractère, c'est en effet n-1 caractère dont on dispose puisque l'on considère le caractère de fin de chaine ('\0') comme faisant partie intégrante de la chaîne".
     Je ne sais pas si je m'avance en disant cela d'ailleurs car aucune règle précise n'est écrite à ce sujet. Mais par usage, on s'apperçoit bien que c'est le cas. Il serait donc préférable que nous ne fassiez pas cas particulier du caractère fin de chaîne... il fait partie des caractères de toutes vos chaînes de caractères et basta.
     De même si vous écrivez des fonctions sur des chaînes de caractères un jour comme une fonction de copie de chaîne par exemple, copier le caractère de fin de chaîne comme si c'était un caractère comme un autre. Car mis à part le fait qu'il marque la fin d'une chaîne de caractère, il en fait partie intérante tout de même. Voilà  :ange:
 
 
     Vous posiez une question que je n'ai pas bien compris :
> Autre question, je reviens sur ma fonction , mais si je veux allouer une taille plus grande à ma chaine, il faut que je lui donne des caractères, non?
 
     D'après l'exemple que vous donnez par la suite, j'ai compris que vous demandiez s'il faut "alimenter" la zone mémoire supplémentaire que l'on a obtenu si la réallocation à réussie. La réponse est évidemment : ça dépend de ce que vous voulez faire !
     Par contre, une chose à savoir c'est que la fonction de réallocation (realloc()) en C, lorsque la réallocation est possible, recopie toutes les données de la zone mémoire originelle vers la nouvelle zone mémoire attribuée (si cette zone a été déplacée et non agrandie tout simplement).
     Du coup, dans le cas de votre chaîne de caractère, tout le contenu de votre chaîne de caractère se retrouve intacte dans la nouvelle zone mémoire qui vous est attribué lorsque la réallocation est réussie. Vous n'avez donc rien à faire !
 
     Le problème serait tout autre si à la place d'une chaîne de caractère, vous aviez un tableau de n objets de type T. Lorsque vous demandez une réallocation de taille m (plus grande) de la table et que la réallocation réussie, les objets de type T qui se trouve en fin de table dont le nombre est m-n ne sont pas initialisés. Selon votre programme et vos besoins, vous auriez alors peut-être tout intérêt à initialiser ses éléments. C'est un exemple comme un autre. Encore une fois, tout dépend de ce que l'on veut faire. Mais ce qui est sûr c'est que vos n premiers éléments de la table se retrouve identique dans la nouvelle table réallouée... heureusement  :sweat:
 
 
>Autre question, si via mon main, j'appelle une fonction bis, qui elle fait appel à la fonction réallocation, comment je dois écrire le passage de l'adresse dans ma fonction bis?
 
     Tout est problème de concordance de types avant tout, puis enfin que ce que vous voulez faire soit bien fait. C'est bête à dire mais voilà, c'est ça. Alors en gros voilà un exemple :
 

Code :
  1. bool reallocation_reussie( char ** s, int n )  /* Réallocation à une taille n de la chaîne de caractères s */
  2. {
  3.   ...
  4. }
  5. bool bis( char ** chaine ) /* Exemple d'utilisation de reallocation */
  6. {
  7.   /* Ici, on dispose de 'chaine' qui est un double pointeur sur un char
  8.    * Donc (*chaine) est un pointeur sur un char : char*
  9.    * Je suppose aussi que chaine n'est pas une table à double dimension
  10.    * mais bel et bien un pointeur sur une chaîne de caractères !
  11.    * Dans ce cas, je dispose déjà du bon "format" de variable pour utiliser reallocation().
  12.    */
  13.   /* Appel de reallocation() */
  14.   return reallocation_reussie( chaine, 1024 );  /* Moi, j'aime bien avoir 1024 caractères dans ma chaîne ! */
  15.   /* Cette fonction bis sert éminemment à... pas grand chose décidément hahaha, enfin presque à rien ;o)~ */
  16. }
  17. int main(...)
  18. {
  19.   char * ma_navette = NULL;
  20.   ... /* J'ai alloué et attribué un nom pour ma navette... */
  21.   /* Maintenant, j'ai besoin de plus de place pour contenir la version 2 du nom de ma navette
  22.    * Réallocation de ma chaine de caractère ma_navette nécessaire sinon scratch.
  23.    * Ici, je dispose d'une chaîne de caractère, et j'ai besoin que le pointeur sur ma chaîne de
  24.    * caractères soit envoyé par "adresse" pour que la fonction de réallocation en change automatiquement
  25.    * la valeur pour moi si la réallocation est réussie
  26.    */
  27.   if (bis( ma_navette ))
  28.   {
  29.     /* Attribué un nouveau nom à ma fusée        */
  30.     /* Autorisé l'étape 2 du protocol de décolage */
  31.   }
  32.   else
  33.   {
  34.     /* Déclencher un signal d'arrêt du décolage... */
  35.     /* Nom version 2 impossible à obtenir.           */
  36.   }
  37. }


 
 
     Voilà. Et encore désolé, j'ai été vraiment concis cette fois-ci, c'est mon record  :kaola:
 
     Encore tous mes voeux de bonne continuation le bossgama  :)


---------------
Marc LY
mood
Publicité
Posté le 06-01-2006 à 16:08:43  profilanswer
 

n°1278285
Pheattarak
Posté le 06-01-2006 à 16:25:35  profilanswer
 

Zut désolé... dans mes appels de fonction, dans la fonction main(), je me suis trompé systématiquement !
 

Code :
  1. int main(...)
  2. {
  3.   ...
  4.   if (reallocation_reussie( &ma_chaine, 1024 ))  /* avec l'adresse de ma_chaine bien sûr !!! donc &ma_chaine */
  5.   ...
  6. }
  7. int main(...)
  8. {
  9.   ...
  10.   if (bis( &ma_navette ))   /* Idem c'est &ma_navette et non pas juste ma_navette */
  11.   ...
  12. }


 
 
     Pardon pardon ! mille pardons !  :whistle:


---------------
Marc LY
n°1279157
bossgama
Posté le 08-01-2006 à 16:25:59  profilanswer
 

haha, merci pour la réponse concise Pheattarak :D  
Désolé pour le retard, mais j'ai eu un weekend chargé. Je crois avoir à peu près compris les notions exposées (reste à les appliquées dans mon projet et c'est pas une mince affaire  :) ).

mood
Publicité
Posté le   profilanswer
 

 Page :   1  2
Page Suivante

Aller à :
Ajouter une réponse
 

Sujets relatifs
[DTD] demande de liens et/ou d'aide[RESOLU] - Aide sur mysql_connect
RUNTIME ERROR BESOIN D'AIDE!aide sur UML
demande d'aideBesoin d'aide VBA Excel
de l'aide pour un projet svp!!!de l'aide pour un projet svp!!!
Demande d'Aide conversion de chaine de caractèresAide pour JBuilder
Plus de sujets relatifs à : aide sur le realloc


Copyright © 1997-2022 Hardware.fr SARL (Signaler un contenu illicite / Données personnelles) / Groupe LDLC / Shop HFR