1. #1
    Candidat au Club
    Femme Profil pro
    Étudiant
    Inscrit en
    avril 2017
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 20
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : avril 2017
    Messages : 15
    Points : 3
    Points
    3

    Par défaut Augmenter la taille d'un buffer

    Bonjour,
    Ça va maintenant faire un bon moment que je reste bloquée sur ce point : je souhaite placer le résultat d'un read dans un buffer, et si le buffer est trop petit, l'augmenter.
    J'ai essayé avec un buffer statique et un dynamique mais ça ne marche toujours pas.
    Voilà une partie de mon code :

    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
     
    char buffer[SIZEBUF];
    ssize_t nb;
    int size=SIZEBUF;       //size prend la taille originale du buffer
    nb=read(fd,buffer,sizeof(buffer));
     
    if(nb==size)
    {
           while (nb>0 && nb==size)
          {
    	size*=2;
    	buffer[size];      //ici je voulais réallouer mon buffer avec une taille plus grande
    	nb=read(fd,buffer,size);
          }
     
    }
    Est-ce que c'est possible en statique ou je dois le faire en dynamique ?


    Merci d'avance pour votre aide,
    Julie

  2. #2
    Membre expert
    Inscrit en
    mars 2005
    Messages
    1 027
    Détails du profil
    Informations forums :
    Inscription : mars 2005
    Messages : 1 027
    Points : 3 026
    Points
    3 026

    Par défaut

    Non, il est impossible de modifier la taille du buffer sur la pile de manière portable. Il faut utiliser realloc. Fais bien attention à l'utiliser correctement, en prenant en charge les cas d'erreur.

  3. #3
    Candidat au Club
    Femme Profil pro
    Étudiant
    Inscrit en
    avril 2017
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 20
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : avril 2017
    Messages : 15
    Points : 3
    Points
    3

    Par défaut

    D'accord merci je vais essayer de le changer en dynamique alors

  4. #4
    Candidat au Club
    Femme Profil pro
    Étudiant
    Inscrit en
    avril 2017
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 20
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : avril 2017
    Messages : 15
    Points : 3
    Points
    3

    Par défaut

    Je vais de changer mon code :
    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
     
    int size=SIZEBUF;
     
    char *buffer=NULL;
    buffer=malloc(SIZEBUF*sizeof(char));
     
    ssize_t nb;
    while(nb=read(fd,buffer,size)> 0 )
         {
    	if(nb==size)
                {
    	      size*=2;
    	      buffer=realloc(buffer,size);
    	      if(buffer==NULL)
    		  {
    		  fprintf(stderr,"erreur realloc");
    		  exit(EXIT_FAILURE);
    		  }
    	    }
        }
    Mais le problème c'est que nb est toujours différents de size, je n'arrive pas à rentrer dans la boucle du if

  5. #5
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    juin 2007
    Messages
    4 935
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : juin 2007
    Messages : 4 935
    Points : 16 360
    Points
    16 360

    Par défaut

    Et si tu affichais size, puis nb à chaque lecture?
    Tu pourrais voir ce qu'il se passe…
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  6. #6
    Membre expert
    Inscrit en
    mars 2005
    Messages
    1 027
    Détails du profil
    Informations forums :
    Inscription : mars 2005
    Messages : 1 027
    Points : 3 026
    Points
    3 026

    Par défaut

    read ne te garantit nullement de retourner le nombre exact d'octets reçu en paramètre (cf. man).


    Citation Envoyé par Matt_Houston Voir le message
    Il faut utiliser realloc. Fais bien attention à l'utiliser correctement, en prenant en charge les cas d'erreur.
    Citation Envoyé par KiitKaate Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    	      buffer=realloc(buffer,size);
    	      if(buffer==NULL)
    		  {
    		  fprintf(stderr,"erreur realloc");
    		  exit(EXIT_FAILURE);
    		  }
    Ton bloc mémoire d'origine est perdu si realloc échoue (cf. man).

  7. #7
    Candidat au Club
    Femme Profil pro
    Étudiant
    Inscrit en
    avril 2017
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 20
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : avril 2017
    Messages : 15
    Points : 3
    Points
    3

    Par défaut

    Citation Envoyé par ternel Voir le message
    Et si tu affichais size, puis nb à chaque lecture?
    Tu pourrais voir ce qu'il se passe…
    Je vois que nb vaut toujours 1, donc c'est normal qu'il ne rentre pas dans la boucle if.
    En fait je cherche à savoir comment savoir que le buffer est plein, mais je ne sais pas comment faire.
    Je pensais que read renvoyait le nombre d'octets lu, donc si c'était égal à size il fallait réallouer.
    Mais apparement ce n'est pas la bonne méthode

  8. #8
    Candidat au Club
    Femme Profil pro
    Étudiant
    Inscrit en
    avril 2017
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 20
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : avril 2017
    Messages : 15
    Points : 3
    Points
    3

    Par défaut

    Citation Envoyé par Matt_Houston Voir le message
    read ne te garantit nullement de retourner le nombre exact d'octets reçu en paramètre (cf. man).






    Ton bloc mémoire d'origine est perdu si realloc échoue (cf. man).
    Oui c'est vrai je viens de créer un buffer tmp, dans lequel je mets le résultat du réalloc. S'il a fonctionné, je recopie tmp dans buffer

  9. #9
    Membre expert
    Inscrit en
    mars 2005
    Messages
    1 027
    Détails du profil
    Informations forums :
    Inscription : mars 2005
    Messages : 1 027
    Points : 3 026
    Points
    3 026

    Par défaut

    Surtout pas ! Relis-bien le man : si l'opération réussit, le bloc mémoire dont l'adresse a été fournie a potentiellement été libéré, son contenu transféré vers le nouveau bloc et l'ancien pointeur ne peut donc être utilisé tant qu'on ne l'a pas réinitialisé. On utilise realloc avec une variable intermédiaire, comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    void *const q = realloc(p, size);
    if (!q) {
        fprintf(stderr, "error: realloc() failed\n");
        goto error;
    }
    p = q;

    Citation Envoyé par KiitKaate Voir le message
    Je vois que nb vaut toujours 1, donc c'est normal qu'il ne rentre pas dans la boucle if.
    En fait je cherche à savoir comment savoir que le buffer est plein, mais je ne sais pas comment faire.
    Je pensais que read renvoyait le nombre d'octets lu, donc si c'était égal à size il fallait réallouer.
    Mais apparement ce n'est pas la bonne méthode
    read renvoie bien le nombre d'octets lus mais si tu veux remplir ton buffer en plusieurs fois, il faut mettre à jour ses arguments à chaque appel - décaler l'adresse de destination et calculer la taille encore disponible - ou tu écrases toujours ce que tu as mis dans le buffer au tour précédent.

  10. #10
    Candidat au Club
    Femme Profil pro
    Étudiant
    Inscrit en
    avril 2017
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 20
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : avril 2017
    Messages : 15
    Points : 3
    Points
    3

    Par défaut

    Oui pour le realloc j'ai fait quelque chose de similaire, j'ai du mal m'exprimer désolé;
    Par contre je ne vois pas comment décaler l'adresse de destination

  11. #11
    Membre expert
    Inscrit en
    mars 2005
    Messages
    1 027
    Détails du profil
    Informations forums :
    Inscription : mars 2005
    Messages : 1 027
    Points : 3 026
    Points
    3 026

    Par défaut

    Mettons qu'au premier appel à read avec l'adresse d'un buffer de taille n, la fonction te renvoie r. Comment lui ferais-tu successivement remplir les n - r octets restants, sans écraser ce qui a déjà été lu ?

    Laisse de côté le cas avec réallocation pour le moment. Un problème à la fois.

  12. #12
    Candidat au Club
    Femme Profil pro
    Étudiant
    Inscrit en
    avril 2017
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 20
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : avril 2017
    Messages : 15
    Points : 3
    Points
    3

    Par défaut

    Matt_Houston :

    Je n'ai jamais fait ça mais je penserais à quelque chose comme
    buffer=&buffer[r]

    Dans ce cas là je dois surement vérifier que je ne dépasse pas la taille du buffer je pense ?
    Mais j'ai quand même l'impression que je vais perdre ce qu'il y a dans le buffer. Est-ce que je dois créer une nouvelle variable ?

  13. #13
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    février 2006
    Messages
    5 828
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : février 2006
    Messages : 5 828
    Points : 16 021
    Points
    16 021
    Billets dans le blog
    1

    Par défaut

    Bonjour
    Citation Envoyé par KiitKaate Voir le message
    Par contre je ne vois pas comment décaler l'adresse de destination
    Te suffit d'écrire à "buf+n"...

    Citation Envoyé par KiitKaate Voir le message
    Dans ce cas là je dois surement vérifier que je ne dépasse pas la taille du buffer je pense ?
    Hum... au pifomètre je penserais que oui !!!

    Citation Envoyé par KiitKaate Voir le message
    Mais j'ai quand même l'impression que je vais perdre ce qu'il y a dans le buffer. Est-ce que je dois créer une nouvelle variable ?
    Mais putain on ne crée pas de nouvelle variable au petit bonheur, on la crée quand on a besoin de mémoriser un truc pour plus tard !!!
    Et ses "impressions" on les remplace par de la "réflexion" pour dérouler en pensée le fonctionnement de son code...

    Donc au lieu de créer de nouvelles variables sans rien savoir de ce qu'on va en faire, on réfléchit à ce qu'il faut gérer. C'est à dire 1) la taille allouée et 2) la taille réellement uilisée (qui ne doit bien évidemment ne jamais dépasser la taille allouée).
    Et (autant faire les choses propres) utiliser une structure sera un plus concernant la lisibilité et l'évolutivité (et notemment se libérer de la contrainte des noms de variables)...

    Exemple

    Code c : 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
    61
    #define SZ_BUF				(100)
    #define SZ_ALLOC			(1024)
     
    typedef struct {
    	char *buffer;
    	char *tmp;
    	size_t nb_elem;
    	size_t sz_alloc;
    } t_alloc;
     
    int main() {
    	t_alloc zone;
    	int nb_lu;
    	char buffer[SZ_BUF];
     
    	...
     
    	// On commence par initialiser les éléments important de la zone (notemment ceux qui nous permettent de détecter si on va dépasser)
    	zone.buffer=NULL;
    	zone.nb_elem=0;
    	zone.sz_alloc=0;
     
    	// La phase de lecture et de remplissage
    	while ((nb_lu=read(fd, buffer, SZ_BUF)) > 0) {
    		// Ici on a un buffer à stocker - On commence par regarder si on a la place de le stocker (ce qui arrive fatalmeent à la première itération puisque tout est à 0)
    		// Et comme la taille prédéfinie peut ne pas suffire, on est obligé de boucler (ce qui n'est pas nécessaire quand on remplit une zone item par item)
    		while ((zone.nb_elem + nb_lu) > zone.sz_alloc) {
    			// Ici on va s'occuper de l'allocation - On commence par augmenter la taille
    			zone.sz_alloc+=SZ_ALLOC;
     
    			// Maintenant on réalloue (si le buffer initial est null, la réallocation se comporte comme une allocation)
    			zone.tmp=realloc(zone.buffer, zone.sz_alloc * sizeof(char));
     
    			// On teste l'allocation
    			if (zone.tmp == NULL) {
    				// Allocation échouée - A prévoir un traitement plus malin qu'un bête "exit" de faignasse. A la limite exit() peut se concevoir mais au-moins libérer ce qui avait été précédemment alloué !!!
    				perror("realloc");
    				free(zone.buffer);
    				zone.buffer=NULL;
    				break;
    			}
     
    			// Allocation réussie - On récupère le pointeur alloué
    			zone.buffer=zone.tmp;
    		}
     
    		// Si l'allocation a échoué
    		if (zone.buffer == NULL) break;
     
    		// ici on a la place d'écrire dans la zone. On y recopie donc le buffer mais au bon endroit (c'est à dire après ce qui a déjà été écrit auparavant)
    		memcpy(zone.buffer + zone.nb_elem, buffer, nb_lu);
     
    		// On n'oublie pas de comptabiliser ce qui a été stocké
    		zone.nb_elem+=nb_lu;
    	}
    	close(fd);
     
    	// Ici le buffer contient tout le fichier (si sa taille le permet bien entendu) - Allez zou, écriture à l'écran
    	if (zone.buffer)
    		write(STDOUT_FILENO, zone.buffer, zone.nb_elem);
    }

    Voilà - Pas besoin de nouvelle variable puisque pour copier x dans y je n'ai besoin que de "x" et de "y".
    Donc j'ai testé sur différents fichiers avec différentes valeurs pour "SZ_ALLOC" (même pour "1") et ça a toujours fonctionné (résultat identique au fichier originel)...
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  14. #14
    Candidat au Club
    Femme Profil pro
    Étudiant
    Inscrit en
    avril 2017
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 20
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : avril 2017
    Messages : 15
    Points : 3
    Points
    3

    Par défaut

    Ok merci beaucoup !
    Pour ma part je n'aurait jamais pensé à faire une structure et quelque chose d'aussi complexe !
    Mais merci d'avoir pris de votre temps

  15. #15
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    février 2006
    Messages
    5 828
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : février 2006
    Messages : 5 828
    Points : 16 021
    Points
    16 021
    Billets dans le blog
    1

    Par défaut

    Citation Envoyé par KiitKaate Voir le message
    Pour ma part je n'aurait jamais pensé à faire une structure
    Ben c'est dommage parce que c'est bien pratique. Ca permet de regrouper ensemble des objets disparates. Et de ne pas avoir à se soucier des noms (je peux tout à fait créer une variable "nb_elem" sans entrer en conflit avec le "nb_elem" de la structure). A la limite j'aurais même dû en faire une aussi pour grouper le buffer et le nb de caractères lus.

    Citation Envoyé par KiitKaate Voir le message
    et quelque chose d'aussi complexe !
    Ben ça semble peut-être complexe mais on ne peut pas faire plus simple: lire, mais avant de stocker commencer par vérifier qu'on a assez de place. Et si ce n'est pas le cas alors commencer par faire de la place. Tout autre façon de faire ne pourra qu'être plus complexe et moins efficace...
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  16. #16
    Membre expert
    Inscrit en
    mars 2005
    Messages
    1 027
    Détails du profil
    Informations forums :
    Inscription : mars 2005
    Messages : 1 027
    Points : 3 026
    Points
    3 026

    Par défaut

    Perso j'utiliserais directement le buffer plutôt qu'un tableau intermédiaire sur la pile et un memcpy, mais soit.

  17. #17
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    février 2006
    Messages
    5 828
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : février 2006
    Messages : 5 828
    Points : 16 021
    Points
    16 021
    Billets dans le blog
    1

    Par défaut

    Citation Envoyé par Matt_Houston Voir le message
    Perso j'utiliserais directement le buffer plutôt qu'un tableau intermédiaire sur la pile
    Mouais. Mais alors on écrit la phase de test+reallocation avant la lecture (après-tout on sait qu'on aura au pire "SZ_BUF" octets de lus) quitte à ce que cette lecture renvoie ensuite 0. Dans ce cas, si la lecture renvoie 0 la reallocation aura alors été faite pour rien et tu auras eu (dans le pire des cas) une reallocation inutile.

    Code c : 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
    	// La phase de lecture et de remplissage
    	while (1) {
    		// Ici on a "à priori" un buffer à stocker - On commence par regarder si on a la place de le stocker (ce qui arrive fatalement à la première itération puisque tout est à 0)
    		while ((zone.nb_elem + SZ_READ) > zone.sz_alloc) {
    			// Ici on va s'occuper de l'allocation - On commence par augmenter la taille
    			zone.sz_alloc+=SZ_ALLOC;
     
    			// Maintenant on réalloue (si le buffer initial est null, la réallocation se comporte comme une allocation)
    			zone.tmp=realloc(zone.buffer, zone.sz_alloc * sizeof(char));
     
    			// On teste l'allocation
    			if (zone.tmp == NULL) {
    				// Allocation échouée
    				perror("realloc");
    				free(zone.buffer);
    				zone.buffer=NULL;
    				break;
    			}
     
    			// Allocation réussie - On récupère le pointeur alloué
    			zone.buffer=zone.tmp;
    		}
     
    		// Si l'allocation a échoué
    		if (zone.buffer == NULL) break;
     
    		// ici on a la place d'écrire dans la zone. On y stocke donc ce qu'on lit dans le fichier mais au bon endroit (c'est à dire après ce qui a déjà été écrit auparavant)
    		if ((nb_lu=read(fd, zone.buffer + zone.nb_elem, SZ_READ)) <= 0) break;
     
    		// On n'oublie pas de comptabiliser ce qui a été lu
    		zone.nb_elem+=nb_lu;
    	}

    Tu as raison, on s'évite une recopie inutile...
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  18. #18
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    juillet 2013
    Messages
    2 080
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : juillet 2013
    Messages : 2 080
    Points : 4 599
    Points
    4 599

    Par défaut

    Sve@r: il y a un truc déglingué dans ton code

    Tu lis SZ_READ octets, mais tu alloues à chaque fois SZ_ALLOC octets en plus (et ton test est bizarre ((zone.nb_elem + SZ_READ) > zone.sz_alloc))


    Moi pour charger un fichier en mémoire, je fais comme cela, en simplifié (en C++, avec du code C):


    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
        std::FILE* file;
        unsigned char* buffer;
        unsigned int count, read_total;
        unsigned char has_no_error;
     
    //  fopen here
     
        buffer = NULL;
        count = 0;
        read_total = 0;
        has_no_error = 1;
     
        while(!feof(file) && has_no_error) {
            count += BUFFER_SIZE;
            buffer = (unsigned char*) realloc(buffer, count * sizeof(char));
     
            if (buffer != NULL) {
                read_total += fread((buffer + count - BUFFER_SIZE), sizeof(char), BUFFER_SIZE, file);
            } else {
                has_no_error = 0;
            }
        }
     
        fclose(file);
     
        if (buffer == NULL) { // <- Maybe useless
    //      error
            return -1;
        }
     
        if (!has_no_error) {
    //      error
            if (buffer != NULL) { free(buffer); }
            return -1;
        }
     
        if (read_total < count) { memset((buffer + read_total), '\0', (count - read_total)); }
     
    //  ...
     
        free(buffer);

  19. #19
    Membre expert
    Inscrit en
    mars 2005
    Messages
    1 027
    Détails du profil
    Informations forums :
    Inscription : mars 2005
    Messages : 1 027
    Points : 3 026
    Points
    3 026

    Par défaut

    Citation Envoyé par Sve@r Voir le message
    Mouais. Mais alors on écrit la phase de test+reallocation avant la lecture (après-tout on sait qu'on aura au pire "SZ_BUF" octets de lus) quitte à ce que cette lecture renvoie ensuite 0. Dans ce cas, si la lecture renvoie 0 la reallocation aura alors été faite pour rien et tu auras eu (dans le pire des cas) une reallocation inutile.
    Le souci, c'est qu'il est impossible de prévoir quand on va rencontrer EOF. On doit consommer le flux jusqu'à ce que l'opération de lecture échoue. L'utilisation de l'interface FILE et de feof comme le suggère mon voisin du dessus n'y change rien : on ne sait qu'on est au bout que lorsque l'on a essayé de lire au moins un octet qui n'est pas là. Même dans le cas où la quantité de données dans le flux correspond exactement à la taille du buffer, il faut un encore un appel système pour confirmer qu'on est à la fin.

    On ne peut donc de toute manière échapper à une allocation, qu'elle se situe sur la pile ou le tas, et même si le flux est vide. D'où ma philosophie : puisqu'il s'agit d'un buffer de taille variable autant la faire sur le tas dès le départ.

    D'une manière générale, mieux vaut blinder le cas courant (la quantité de données dans le flux est non nulle et n'est pas un multiple de la taille du buffer), plutôt que d'optimiser pour des cas extrêmes, et extrêmement rares.

  20. #20
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    février 2006
    Messages
    5 828
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : février 2006
    Messages : 5 828
    Points : 16 021
    Points
    16 021
    Billets dans le blog
    1

    Par défaut

    Citation Envoyé par foetus Voir le message
    Sve@r: il y a un truc déglingué dans ton code
    Jamais

    Citation Envoyé par foetus Voir le message
    Tu lis SZ_READ octets, mais tu alloues à chaque fois SZ_ALLOC octets en plus
    Exact. Indépendances des valeurs. SZ_READ c'est le nb d'octets lus et SZ_ALLOC c'est le bloc mémoire alloué. Bon en fait ce "SZ_READ" est issu de mon précédent code où j'avais un buffer d'une taille fixe mais ça fonctionne aussi sans.

    Citation Envoyé par foetus Voir le message
    (et ton test est bizarre ((zone.nb_elem + SZ_READ) > zone.sz_alloc))
    Ben c'est la façon de savoir si la taille est suffisante pour stocker ce qui sera lu. Comme c'est du futur je regarde donc si la taille déjà remplie plus le nb d'octets lus après rentre dans la zone allouée.
    Imaginons que j'ai une taille SZ_BUF de 10 et que je lise par SZ_READ de 4. La zone au départ fait nb_elem=0 et sz_alloc=0. Fatalement le test est vrai donc je rentre dans la partie realloc. sz_alloc passe à 10. Puis j'en sors et je lis 4. Voici alors le déroulement
    sz_alloc=0 et nb_elem=0 => test (0 + 4) > 0 vrai => allocation => sz_alloc passe à 10. Puis je lis 4 et stocke 4. nb_elem passe à 4
    sz_alloc=10 et nb_elem=4 => test (4 + 4) > 10 faux donc je ne fais que lire sans allouer. Je lis 4 et stocke 4. nb_elem passe à 8
    sz_alloc=10 et nb_elem=8 => test (8 + 4) > 10 vrai => allocation => sz_alloc passe à 20. Puis je lis 4 et stocke 4. nb_elem passe à 12
    sz_alloc=20 et nb_elem=12 => test (12 + 4) > 20 faux donc je ne fais que lire sans allouer. Je lis 4 et stocke 4. nb_elem passe à 16
    sz_alloc=20 et nb_elem=16 => test (16 + 4) > 20 faux donc je ne fais que lire sans allouer. Je lis 4 et stocke 4. nb_elem passe à 20
    sz_alloc=20 et nb_elem=20 => test (20 + 4) > 20 vrai => allocation => sz_alloc passe à 30. Puis je lis 4 et stocke 4. nb_elem passe à 24
    etc...

    L'algo fonctionne aussi même si SZ_READ > SZ_ALLOC parce que ce test est fait dans une boucle qui recommence jusqu'à ce que ça convienne . Prenons SZ_READ=20 et SZ_ALLOC=6 et repartons du début
    sz_alloc=0 et nb_elem=0 => test (0 + 20) > 0 vrai => allocation => sz_alloc passe alors à 6 puis 12 puis 18 puis 24 (la boucle). Puis je lis 20 et stocke 20. nb_elem passe à 20
    sz_alloc=24 et nb_elem=20 => test (20 + 20) > 24 vrai => allocation => sz_alloc passe alors à 30 puis 36 puis 42. Puis je lis 20 et stocke 20. nb_elem passe à 40
    sz_alloc=42 et nb_elem=40 => test (40 + 20) > 42 vrai => allocation => sz_alloc passe alors à 48 puis 54 puis 60. Puis je lis 20 et stocke 20. nb_elem passe à 60
    etc.

    Citation Envoyé par foetus Voir le message
    Moi pour charger un fichier en mémoire, je fais comme cela, en simplifié (en C++, avec du code C):


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
        ...
        while(!feof(file) && has_no_error) {
        ...
        }
    feof() ne sert pas à ça. feof() ne détecte pas une fin de fichier, il détecte si, une fois que tu ne peux plus lire le fichier (donc comme le dis Matt_Houston, une fois que tu as tenté de lire un octet de plus que ce qu'il y en a dans le fichier), à indiquer si la cause de la "non-lecture" est due à une fin (tentative de lire un de plus) ou à autre chose (panne disque, etc...)

    Avec ton test, tu fais ton traitement une fois de trop...

    Exemple
    Code c : 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
    #include <stdio.h>
    #include <string.h>
     
    int main()
    {
    	char ligne[1024];
    	FILE *fp;
    	char *c;
     
    	fp=fopen("/etc/passwd", "r");
    	while (!feof(fp)) {
    		fgets(ligne, 1024, fp);
    		if ((c=strchr(ligne, '\n')) != 0) *c=0;
    		printf(%p - "ligne=[%s]\n", c, ligne);
    	}
     
    	fclose(fp);
    }

    Ce code affiche deux fois la dernière ligne. Parce que, quand j'ai lu la dernière, le système ne sait pas que je suis à la dernière. le !feof() reste vrai et je fais une lecture de plus, qui échoue mais je ne la détecte pas, donc j'affiche le buffer précédent qui n'a pas été modifié. Et ça on le voit parce que j'affiche aussi "c" qui me sert à détecter et à supprimer le '\n'. A chaque lecture le '\n' y est donc c n'est pas nul, mais à la lecture "après" le buffer n'ayant pas été modifié mais ayant déjà eu son '\n' de remplacé par un 0, le c est null.

    Si on veut que ça fonctionne il faut tester le fgets() (ou la fonction de lecture utilisée). Et donc le feof() ne sert plus...
    Code c : 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
    #include <stdio.h>
    #include <string.h>
     
    int main()
    {
    	char ligne[1024];
    	FILE *fp;
    	char *c;
     
    	fp=fopen("/etc/passwd", "r");
    	while (fgets(ligne, 1024, fp)) {
    		if ((c=strchr(ligne, '\n')) != 0) *c=0;
    		printf("(%p) ligne=[%s]\n", c, ligne);
    	}
     
    	fclose(fp);
    }
    Et ensuite, après la boucle on peut alors utiliser feof pour indiquer si l'arrêt de la lecture est normale ou pas...

    Sinon pour ta méthode d'allocation oui ça fonctionne puisque tu lis "n" et tu alloues "n". Avec un risque de réallocation "en rafales" si n vaut 1. C'est pas vraiment grave sauf que la réallocation est quand-même un processus gourmand (obligation de trouver un espace dispo puis déplacement si le nouvel espace n'est pas au même endroit que le précédent) donc je préfère optimiser via des réallocations "par bloc". Ainsi je montre montre comment faire quand le "bloc" d'allocation est indépendant de la taille lue.
    C'est vrai que c'est "un petit peu exagéré" surtout pour une lecture de "n>1" octets où je peux faire coïncider "n" et "SZ_ALLOC"). En fait, j'ai dérivé cet algo (tapé pour l'occasion) d'un algo plus classique où je lis "1" et où j'alloue "n". Dans ce cas, pas besoin de boucle de réallocation. Un simple if (zone.nb_elem == zone.sz_alloc) suffit.
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

Discussions similaires

  1. stringstream : augmenter la taille initiale du buffer
    Par jeanmarcL dans le forum SL & STL
    Réponses: 2
    Dernier message: 17/04/2007, 21h10
  2. comment augmenter la taille d'un tableau ?
    Par salseropom dans le forum C
    Réponses: 5
    Dernier message: 16/12/2005, 12h47
  3. augmenter la taille d'une tablespace?
    Par sali dans le forum Oracle
    Réponses: 1
    Dernier message: 01/12/2005, 15h52
  4. [Redo log] : augmenter la taille des fichiers
    Par user_oracle dans le forum Oracle
    Réponses: 3
    Dernier message: 29/11/2005, 19h49
  5. []Augmenter la taille de la pile des appels ?
    Par oncle ervil dans le forum VB 6 et antérieur
    Réponses: 5
    Dernier message: 10/05/2005, 09h29

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