fonction saisie ligne sans limite

fonction saisie ligne sans limite - C - Programmation

Marsh Posté le 18-12-2007 à 21:46:03    

Bonjour, j'essaie de faire une fonction qui me permette de récupérer une ligne entrée au clavier sans le '\n' final.
 
Mon but est de faire une fonction qui n'ai pas besoin de limite dans la taille de la ligne à saisir. J'ai donc pensé à realloc, je trouve ça bourrin, mais ça marche:
 

Code :
  1. char * getlineStdin(void)
  2. {
  3.     char * strIn = NULL;
  4.     char c;
  5.     size_t count = 0;
  6.     while((c = getchar()) != '\n' && c != EOF)
  7.     {
  8.         strIn = realloc(strIn,(count+1));
  9.         if(strIn == NULL) return NULL;
  10.         strIn[count] = c;
  11.         count++;
  12.     }
  13.     strIn = realloc(strIn,count+1);
  14.     if(strIn == NULL) return NULL;
  15.     strIn[count] = '\0';
  16.     return strIn;
  17. }


 
Je compte l'utiliser souvent donc j'aimerais bien avoir des retours SVP, merci d'avance.


Message édité par ngkreator le 18-12-2007 à 21:48:53
Reply

Marsh Posté le 18-12-2007 à 21:46:03   

Reply

Marsh Posté le 18-12-2007 à 22:48:56    

Tu devrais éviter de faire un realloc() à chaque char, c'est très pénalisant. Fait plutôt tes reallocs() par blocs : quand tu n'as plus de place dans le buffer tu lui ajoutes un bloc de 4096 octets par exemple, et ensuite tu es tranquile pour 4096 getchar().
 
Ensuite une fois que tu realloc() par bloc, tu peux aussi faire les lectures par blocs avec fgets(). Ca t'évitera autant d'appels de fonction.
 
Enfin, si ton système dispose de getline(), c'est exactement ce que tu veux.

Reply

Marsh Posté le 19-12-2007 à 08:24:39    

On peut commencer par 4096 et il est conseillé pour la reallocation de multiplier la taille par le nombre d'or.

Reply

Marsh Posté le 19-12-2007 à 10:13:16    

La fonction que j'avais créée à cette fin, je l'utilisais pour lire des fichiers de données, et donc plutôt que de refaire à chaque ligne lue les mêmes phases de réallocations, je stockais la taille du bloc dans un static.  
La taille initiale était assez petite (dans les 100-200 je crois), à chaque réallocation je doublais, et je stockais la nouvelle taille. Comme ça, à chaque allocation j'allouais le plus gros bloc déjà alloué auparavant, le but étant de minimiser le nombre de realloc() lors de la lecture de nombreuses lignes de tailles similaires.


Message édité par Elmoricq le 19-12-2007 à 10:13:50
Reply

Marsh Posté le 19-12-2007 à 10:37:48    

Bonjour,
 

Code :
  1. strIn = realloc(strIn,count+1);
  2. if(strIn == NULL) return NULL;


 
Il y a une fuite de mémoire si 'realloc' échoue, il me semble...
Extrait du man :


realloc() renvoie un pointeur sur la mémoire nouvellement allouée, qui est correctement alignée pour  n'importe
quel  type  de variable, et qui peut être différent de ptr, ou NULL si la demande échoue, ou si size vaut zéro.
Si realloc() échoue, le bloc mémoire original reste intact, il n'est ni libéré ni déplacé.


Reply

Marsh Posté le 19-12-2007 à 10:41:09    

Trap D a écrit :

On peut commencer par 4096 et il est conseillé pour la reallocation de multiplier la taille par le nombre d'or.


Seulement les jours de pleine lune ;)

Reply

Marsh Posté le 19-12-2007 à 10:48:37    

spotaszn a écrit :

Il y a une fuite de mémoire si 'realloc' échoue, il me semble...


 
Il te semble bien. :jap:
Pour correctement utiliser realloc(), si l'on souhaite poursuivre le programme après échec, il faut effectivement passer par une variable temporaire, ce qui laisse le pointeur initialement utilisé intact.

Reply

Marsh Posté le 19-12-2007 à 13:04:42    

Merci pour toutes vos réponses!
 

  • allocation par bloc -> ok mais je vais faire ça avec des plus petits bloc, c'est pour lire des caractères écrits aux clavier donc j'ai besoin de beaucoup moins de place.
  • fonction getline -> je connais celles du C++ mais pas pour C.
  • fuite de mémoire -> ok pour la variable temporaire.

Reply

Marsh Posté le 19-12-2007 à 14:32:43    

matafan a écrit :


Seulement les jours de pleine lune ;)


Plutôt avec un allocateur best fit. Mais osef, on parle de quelques octets. 4096 c'est peut être excessif cependant comme départ.

Reply

Marsh Posté le 19-12-2007 à 14:33:32    

ngkreator a écrit :

Merci pour toutes vos réponses!

  • fonction getline -> je connais celles du C++ mais pas pour C.

y a getline en C mais pas standard, extension GNU.

Reply

Marsh Posté le 19-12-2007 à 14:33:32   

Reply

Marsh Posté le 19-12-2007 à 14:44:57    

Ah oui donc je vais rester sur du standard.
 

Code :
  1. char * getlineStdin(void)
  2. {
  3.     size_t size = GETLINE_STDIN_INPUT_SIZE;
  4.     char * strIn = NULL;
  5.     char * strTmp = NULL;
  6.     char c;
  7.     size_t count = 0;
  8.     strIn = malloc(size);
  9.     if(strIn != NULL)
  10.     {
  11.         while((c = getchar()) != '\n' && c != EOF && strIn != NULL)
  12.         {
  13.             if(count < size-1)
  14.                 strIn[count] = c;
  15.             else
  16.             {
  17.                 size *= 2;
  18.                 strTmp = (char*) realloc(strIn,size);
  19.                 if(strTmp != NULL)
  20.                 {
  21.                     strIn = strTmp;
  22.                     strIn[count] = c;
  23.                 }
  24.                 else
  25.                 {
  26.                     free(strIn);
  27.                     strIn = NULL;
  28.                 }
  29.             }
  30.             count++;
  31.         }
  32.         if(strIn != NULL)
  33.             strIn[count] = '\0';
  34.     }
  35.     return strIn;
  36. }


 
Porblème:  
 

Code :
  1. strTmp = (char*) realloc(strIn,size);
  2. if(strTmp != NULL)
  3.     strIn = strTmp;


 
Si le bloc mémoire pointé par strIn est déplacé (lors de la réallocation) de l'emplacement mémoire A vers B on pourrait libérer A. Mais on ne sais pas quand il y a eu déplacement ou non. On ne peut donc pas faire:
 

Code :
  1. strTmp = (char*) realloc(strIn,size);
  2. if(strTmp != NULL)
  3. {
  4.     free(strIn);
  5.     strIn = strTmp;
  6. }


 
Au grand risque de libérer A alors que strTmp pointe également vers A.
 
Comment faire?
 
Il faut aussi que je gère la vidange du stdin avant de commencer d'ailleurs.


Message édité par ngkreator le 19-12-2007 à 14:58:27
Reply

Marsh Posté le 20-12-2007 à 01:27:07    

Attention, le realloc "libère" de lui-même (ou plutôt remet à disposition dans la heap) la mémoire originelle du pointeur si la ré-allocation c'est bien passé. D'ailleurs dans le cas présent, il ne faut surtout pas essayer de faire un free sur le strIn, qui pointe vers l'ancienne zone mémoire (voir pire la même) et cela fera ce que l'on appelle de la memory corruption.
 
donc pour moi ton code initial est bon et ne comporte pas de fuite.
 
Par contre, pour la beauté du geste, je n'aime pas écrire 2 fois le même code,comme la ligne suivante qui y est 2 fois  :

Code :
  1. strIn[count] = c;


 
en réflechissant un peu, tu verras qu'il est possible de d'abord mettre le caractère dans le tableau, faire le ++ et ensuite (avec la bonne condition, une simple égalité) faire si nécessaire l'augmentation de la taille du buffer...
Le if initial est inutile, surtout si tu réordonance un peu la condition dans le while (si ton souhait est d'éviter un getchar dont tu ne ferais rien)
 
D'ailleurs, la politique du *=2 me parrait un peu violente, mais c'est un choix
 
Mais tout cela n'est que détail :)
 
Au final je verais qqch du style  

Code :
  1. char * getlineStdin(void)
  2. {
  3.      size_t size = GETLINE_STDIN_INPUT_SIZE;
  4.      char * strIn = NULL;
  5.      char * strTmp = NULL;
  6.      char c;
  7.      size_t count = 0;
  8.      strIn = malloc(size);
  9.      while ( [la bonne condition] ) {
  10.            strIn[count] = c;
  11.            count++;
  12.            if([la bonne condition]){
  13.                  size *= 2; /* éventuellement remplacer par un += GETLINE_STDIN_INPUT_SIZE */
  14.                  strTmp = (char*) realloc(strIn,size);
  15.                  if(strTmp==NULL) free(strIn);
  16.                  strIn=strTmp; /*c'est inutilement sioux, je sais, faire le else proprement, c'est + lisible*/
  17.            }
  18.     }
  19.     if(strIn!=NULL){
  20.         strIn[count]="\0";
  21.     }
  22.     return strIn;
  23. }


 
Bon courage


Message édité par dreameddeath le 20-12-2007 à 01:27:39
Reply

Sujets relatifs:

Leave a Replay

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