IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

Scanf et allocation dynamique


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Nouveau membre du Club
    Inscrit en
    Août 2009
    Messages
    4
    Détails du profil
    Informations forums :
    Inscription : Août 2009
    Messages : 4
    Par défaut Scanf et allocation dynamique
    Bonjour bonjour,
    Grande question existentielle...
    Je cherche à récupérer une chaîne de caractères sans savoir la longueur de celle-ci.
    Existe t-il un moyen d'utiliser scanf et puis, en fonction de l'entrée, d'allouer une certaine taille de mémoire afin de ne pas devoir gaspiller d'espace en déclarant un méga grand tableau de char avant?
    Merci!

  2. #2
    Invité(e)
    Invité(e)
    Par défaut
    Bonjour,

    Tel que tu le décris, ça n'existe pas.

    En revanche, tu peux tout à fait simuler ce fonctionnement avec un usage de realloc / getchar.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    char *buf = NULL;
    size_t size = 0;
    while(1) {
        char *tmp;
        /* récupération du caractère suivant */
        int c = getchar();
        ++size;
        /* on agrandi la chaine */
        tmp = realloc(buf, size);
        if(tmp != NULL) { 
            buf = tmp;
            /* on ajoute le caractère */
            buf[size - 1] = c;
            if(c == '\n' || c == EOF) {
                /* caractère spécial : on le remplace par 0 
                   et on sort de la boucle while */
                buf[size - 1] = '\0';
                break;
            }
        } else {
            fprintf(stderr, "error realloc...\n");
            break;
        }
    }
     
    /* utiliser la chaine ici*/
     
    /* ménage */
    if(buf != NULL) 
    {
        free(buf);
    }
    /!\ Code pas testé.

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Août 2009
    Messages
    65
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 65
    Par défaut
    J'ai trouvé un exemple avec fgets dans le bouquin de Christophe Blaess (chapitre 10) qui fait à peu près ça. C'est à peu près le code que j'utilise dans mes premiers pas pour le moment. Si ça peut aider.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    /***********************************************************************\
       exemple_fgets
     
       Programme d'exemple du livre "Developpement systeme en C sous Linux"
       
       (c) 2000,2005 - Christophe Blaess
     
    \***********************************************************************/
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    	char * alloc_fgets (FILE * fp);
     
    	int
    			main (void)
    {
    	char * chaine;
     
    	while (1) {
    		chaine = alloc_fgets(stdin);
    		if (chaine == NULL)
    			/* Pas assez de mémoire */
    			break;
    		if ((chaine[0] == '\n') || (chaine[0] == '\0'))
    			/* Chaine vide... on quitte */
    			break;
    		fprintf(stdout, "%d caractères\n", strlen(chaine));
    		free(chaine);
    	}
    	return EXIT_SUCCESS;
    }
     
     
    	char *
    			alloc_fgets (FILE * fp)
    {
    	char *	retour = NULL;
    	char *  a_ecrire = NULL;
    	int	taille = 64;
     
    	retour = malloc (taille);
    	a_ecrire = retour;
     
    	while (1) {
    		if (fgets (a_ecrire, 64, fp) == NULL)
    			break;
    		if (strlen (a_ecrire) < 63)
    			break;
    		/* On se place sur le caractère nul final */
    		a_ecrire = a_ecrire + 63; 
    		/* Et l'on agrandit également le buffer de 63 caractères */
    		taille += 63;
    		retour = realloc (retour, taille);
    		if (retour == NULL)
    			break;
    	}
    	return (retour);
    }

  4. #4
    Invité(e)
    Invité(e)
    Par défaut
    Citation Envoyé par vitoubien Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
        retour = realloc (retour, taille);
        if (retour == NULL)
            break;
        }
    Attention, dans ce qui est écrit ci dessus : si realloc échoue, la première chaine est perdue ; on a alors une fuite mémoire.


    Bien utiliser realloc()
    .
    Dernière modification par Deepin ; 22/07/2011 à 08h00.

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Août 2009
    Messages
    65
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 65
    Par défaut
    Citation Envoyé par mabu Voir le message
    Attention, dans ce qui est écrit ci dessus : si realloc échoue, la première chaine est perdue ; on a alors une fuite mémoire.


    Bien utiliser realloc()
    .


    Merci mabu !
    Zul m'avait déja pointé ce souci avec realloc mais je n'avais pas bien compris le problème.
    Mais quid de la fuite mémoire ? Peux-tu expliciter un peu plus s'il te plait ?

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Février 2006
    Messages
    43
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 43
    Par défaut
    La fuite de memoire provient du fait que lorsque tu fais un realloc, tu alloues une nouvelle zone memoire dans laquelle tu copies la zone actuellement pointe par ton pointeur, pour enfin liberer ton pointeur.
    Dans le cas ou le realloc fail, il retourne un pointeur NULL. Cela dit, pour eviter de perdre les donnees, l'implementation a ete fete de sorte que realloc ne free le pointeur que tu lui donnes que lorsque la copie a ete effectue avec succes.

    Donc si le realloc fail, tu perds ton pointeur, et la memoire n'est pas liberee, d'ou la fuite de memoire

  7. #7
    Membre averti
    Profil pro
    Inscrit en
    Février 2006
    Messages
    43
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 43
    Par défaut
    Cela dit (en partant sur cette methode), realloc n'est tres performant, donc si je peux amener une petite amelioration, qui consiste a reallouer par block, ce qui amene un realloc tous les 255 charactere au lieu de chaque caractere (tu peux augmenter le define dependant de la moyenne de la longueur des string que le user va rentrer).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
     
    #define SYZE 255
     
     
    char *buf = (char *)malloc(sizeof(char) * SYZE);
    size_t size = SYZE;
    size_t current_size = 0;
     
    while(1) {
        char *tmp;
        /* récupération du caractère suivant */
        int c = getchar();
        ++current_size ;
     
        if(current_size == size)
        {
            size += SIZE;
            /* on agrandi la chaine */
            tmp = realloc(buf, size);
        }
     
        if(tmp != NULL) { 
            buf = tmp;
            /* on ajoute le caractère */
            buf[size - 1] = c;
            if(c == '\n' || c == EOF) {
                /* caractère spécial : on le remplace par 0 
                   et on sort de la boucle while */
                buf[size - 1] = '\0';
                break;
            }
        } else {
            fprintf(stderr, "error realloc...\n");
            break;
        }
    }
     
    /* utiliser la chaine ici*/
     
    /* ménage */
    if(buf != NULL) 
    {
        free(buf);
    }
    /!\ Code toujours pas testé.


    Bon je chipote un peu...


    Edit: Grilled avec sensiblement la meme methode...

  8. #8
    Invité(e)
    Invité(e)
    Par défaut
    Citation Envoyé par wadabush Voir le message
    Cela dit (en partant sur cette methode), realloc n'est tres performant, donc si je peux amener une petite amelioration, qui consiste a reallouer par block, ce qui amene un realloc tous les 255 charactere au lieu de chaque caractere (tu peux augmenter le define dependant de la moyenne de la longueur des string que le user va rentrer).
    Oui, realloc n'est pas forcement performant, mais à l'échelle d'un utilisateur qui tape sur son clavier, je ne suis pas sur que cela soit trop perceptible.

    Si on veut éviter trop d'appels à realloc et éviter de gâcher de la ram, la meilleur méthode serait à mon avis une allocation par paquet de 256 octets (comme ici) suivit d'une réduction de la taille du buffer à la taille qui va bien.

    Au passage, le dernier code posté pose problème : tmp n'est pas initialisé,or on affecte buf à tmp à chaque tour.

  9. #9
    Membre averti
    Profil pro
    Inscrit en
    Août 2009
    Messages
    65
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 65
    Par défaut
    Petit retour en arrière sur l'exemple avec fgets que je fournissais, le retour à la ligne est également récupéré dans la chaine résultante.

    Alors dans les programmes basiques où j'utilise ce code pour ne récupérer qu'une seule entrée au clavier et non un texte entier en entrée standard, j'ai ajouté avant le return de la fonction la ligne suivante:
    retour[strlen(retour) - 1]='\0';

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. probleme d'allocation dynamique
    Par vince3320 dans le forum C
    Réponses: 10
    Dernier message: 22/04/2004, 16h27
  2. petit pbm allocation dynamique de stringGrid
    Par AnneOlga dans le forum C++Builder
    Réponses: 10
    Dernier message: 17/01/2004, 11h59
  3. Allocation dynamique de structures
    Par fr_knoxville dans le forum C
    Réponses: 8
    Dernier message: 06/05/2003, 21h59
  4. Allocation dynamique de mémoire en asm
    Par narmataru dans le forum Assembleur
    Réponses: 7
    Dernier message: 17/12/2002, 22h31
  5. Réponses: 4
    Dernier message: 03/12/2002, 16h47

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo