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 :

comportement de malloc


Sujet :

C

  1. #1
    Membre averti
    Inscrit en
    Janvier 2004
    Messages
    25
    Détails du profil
    Informations forums :
    Inscription : Janvier 2004
    Messages : 25
    Par défaut comportement de malloc
    Bonjour !

    Aujourd'hui, j'ai un problème de malloc.

    Donc j'ai une fonction qui récupère des chaines de caractères pour se connecter a une BD. L'ensemble se passe plutot bien, sauf quand les chaines de carctères font une certaine longueur (?)
    Voici le code incriminé :
    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 Query_update_settings(PGconn * conn, const char ** paramValues)
    {	PGresult * res;
    	int len_str;
    	char * query;
    	fprintf(stderr,"%s %s %d\n", paramValues[1], paramValues[0], strlen(paramValues[1])); /* imprime : "nb_max_users 0 12" */
    	len_str = strlen(paramValues[1]);
    	fprintf(stderr,"avant malloc param : %s\n", paramValues[1]); /* imprime : "avant malloc param : nb_max_users" */
    	malloc(len_str+3);
    	fprintf(stderr,"apres malloc param : %s\n", paramValues[1]); /* imprime : "apres malloc param : nb_max_users^Y" */
    		if ((query = malloc(strlen(paramValues[1]) + strlen(paramValues[0]) + 50)) != NULL) {
    		fprintf(stderr,"%s %s %d\n", paramValues[1], paramValues[0], strlen(paramValues[1]));
    		strcpy(query,"UPDATE settings SET value = '");
    		strcat(query,paramValues[0]);
    		strcat(query,"' WHERE name = '");
    		strcat(query,paramValues[1]);
    		fprintf(stderr,"%s %d\n", paramValues[1], strlen(paramValues[1]));
    		strcat(query,"'");
                    fprintf(stderr,"query : %s \n", query);
    		}
    Les lignes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
                   fprintf(stderr,"%s %s %d\n", paramValues[1], paramValues[0], strlen(paramValues[1])); /* imprime : "nb_max_users 0 12" */
    		len_str = strlen(paramValues[1]);
    		fprintf(stderr,"avant malloc param : %s\n", paramValues[1]); /* imprime : "avant malloc param : nb_max_users" */
    		malloc(len_str+3);
    		fprintf(stderr,"apres malloc param : %s\n", paramValues[1]); /* imprime : "apres malloc param : nb_max_users^Y" */
    ne servent absolument a rien, c'était juste pour vérifier que le problème vient bien du malloc.

    Donc, sans ces lignes, j'ai juste if (query = malloc(strlen ...)), ce qui fonctionne bien, sauf quand les chaines contenues dans paramValues[1] font 12 ou 13 ou 28 ou 29 ou 60 ou 61 (je n'ai pas cherché plus loin), auquel cas j'ai un caractère en trop ou modifié (le caractère 13 est ajouté, ou modifié par exemple). Lorsque la chaine fait 5 caractères, ou 10, j'obtiens bien le bon résultat ....

    Voila, en fait je ne sais même pas où cherché ? Est-ce que je n'ai rien compris a malloc, ou est-ce un bug du compilateur, de la librairie, d'autre chose ? Un comportement normal ?


    Une théorie (pondue par ma copine ! :-) ) :

    Lorsque malloc est utilisé avec une certaine longueur de chaine, soit il ajoute un caractere aleatoire a la fin soit il remplace le dernier. Après une petite analyse, voici ma théorie :
    si on transforme la longueur de la chaine en binaire, le probleme intervient lorsque les 2 et 3eme bits de poids faibles sont à 1 et le premier à 0.
    Explication :
    chaine de longueur 11 -> 1011 => tout va bien
    chaine de longueur 12 -> 1100 => malloc rajoute un caractère
    chaine de longueur 13 -> 1101 => malloc remplace le dernier caractère
    chaine de longueur 14 -> 1110 => tout va bien
    Si on rajoute un bit, on a la meme chose :
    chaine de longueur 28 -> 11100 => malloc rajoute un caractère
    chaine de longueur 29 -> 11101 => malloc remplace le dernier caractère
    chaine de longueur 60 -> 111100 => malloc rajoute un caractère
    chaine de longueur 61 -> 111101 => malloc remplace le dernier caractère
    Par contre, pour des chaines de longueurs 27,30,59, ou 62, ma chaine ne bouge pas.

    Y-a-t-il une utilisation particulière de la mémoire qui pourrait provoquer ce genre de truc ?

    Merci !

    Jeremy

  2. #2
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut Re: comportement de malloc
    Citation Envoyé par jems
    Donc j'ai une fonction qui récupère des chaines de caractères pour se connecter a une BD. L'ensemble se passe plutot bien, sauf quand les chaines de carctères font une certaine longueur (?)
    Juste au cas où, un petit rappel : "une chaine est un tableau de char terminé par un 0".
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    	malloc(len_str+3);
    ??? As-tu la doc de malloc () ? cette fonction retourne l'adresse du bloc alloué. Or tu ne la stockes pas? Tu t'attends à quoi au juste ? De la magie ?

  3. #3
    Membre averti
    Inscrit en
    Janvier 2004
    Messages
    25
    Détails du profil
    Informations forums :
    Inscription : Janvier 2004
    Messages : 25
    Par défaut
    ??? As-tu la doc de malloc () ? cette fonction retourne l'adresse du bloc alloué. Or tu ne la stockes pas? Tu t'attends à quoi au juste ? De la magie ?
    Non, pas a de la magie :

    Les lignes :
    Code:

    fprintf(stderr,"%s %s %d\n", paramValues[1], paramValues[0], strlen(paramValues[1])); /* imprime : "nb_max_users 0 12" */
    len_str = strlen(paramValues[1]);
    fprintf(stderr,"avant malloc param : %s\n", paramValues[1]); /* imprime : "avant malloc param : nb_max_users" */
    malloc(len_str+3);
    fprintf(stderr,"apres malloc param : %s\n", paramValues[1]); /* imprime : "apres malloc param : nb_max_users^Y" */

    ne servent absolument a rien, c'était juste pour vérifier que le problème vient bien du malloc.
    le code original, avant essai de débogage ressemble a ca :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    int Query_update_settings(PGconn * conn, const char ** paramValues)
    {	char * query;
    		if ((query = malloc(strlen(paramValues[1]) + strlen(paramValues[0]) + 50)) != NULL) {
    		strcpy(query,"UPDATE settings SET value = '");
    		strcat(query,paramValues[0]);
    		strcat(query,"' WHERE name = '");
    		strcat(query,paramValues[1]);
    		strcat(query,"'");
    		fprintf(stderr,"query : %s \n", query);
                   }
    }
    Désolé si je me suis mal exprimé, ou si la méthode de débogage n'est pas bonne ...

  4. #4
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par jems
    le code original, avant essai de débogage ressemble a ca :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    int Query_update_settings(PGconn * conn, const char ** paramValues)
    {	char * query;
    		if ((query = malloc(strlen(paramValues[1]) + strlen(paramValues[0]) + 50)) != NULL) {
    		strcpy(query,"UPDATE settings SET value = '");
    		strcat(query,paramValues[0]);
    		strcat(query,"' WHERE name = '");
    		strcat(query,paramValues[1]);
    		strcat(query,"'");
    		fprintf(stderr,"query : %s \n", query);
                   }
    }
    Désolé si je me suis mal exprimé, ou si la méthode de débogage n'est pas bonne ...
    Le '50' Il vient de quoi ? J'utilise la technique suivante pour vérifier les débordements de chaines.

    Deux macros :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    #define LIM_PTR(p,l) (p)[(l) -1]=0
    #define CHK_PTR(p,l) assert((p)[(l) -1]==0)
    Que j'utilise comme ceci:
    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
     
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <assert.h>
     
    #define LIM_PTR(p,l) (p)[(l) -1]=0
    #define CHK_PTR(p,l) assert((p)[(l) -1]==0)
     
    typedef struct
    {
       int dummy;
    }
    PGconn;
     
    int Query_update_settings (PGconn * conn, const char **paramValues)
    {
       size_t size = strlen (paramValues[1]) + strlen (paramValues[0]) + 50;
       char *query = malloc (size);
       if (query != NULL)
       {
          LIM_PTR (query, size);
     
          strcpy (query, "UPDATE settings SET value = '");
          CHK_PTR (query, size);
     
          strcat (query, paramValues[0]);
          CHK_PTR (query, size);
     
          strcat (query, "' WHERE name = '");
          CHK_PTR (query, size);
     
          strcat (query, paramValues[1]);
          CHK_PTR (query, size);
     
          strcat (query, "'");
          CHK_PTR (query, size);
     
          fprintf (stderr, "query : %s \n", query);
     
          free (query), query = NULL;
       }
       return 0;
    }
     
    int main_ (void)
    {
       PGconn conn;
     
       static char const *paramValues[] =
       {
          "123",
          "45678",
       };
     
       Query_update_settings (&conn, paramValues);
       return 0;
    }
    Apparament, il n'y a pas de problème de débordement. malloc() fonctionne comme on s'y attend, et on obtient
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    query : UPDATE settings SET value = '123' WHERE name = '45678'
    C'était quoi ton problème déjà ? Ok. J'ai relu ton post, je n'ai pas de problèmes avec ces paramètres :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
       static char const *paramValues[] =
       {
          "abcdefghijklmnopqrstuvwxyz"
          "abcdefghijklmnopqrstuvwxyz"
          "abcdefghijklmnopqrstuvwxyz"
          "abcdefghijklmnopqrstuvwxyz"
          ,
          "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
          "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
          "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
          "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
          ,
       };
    Et j'obtiens:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    query : UPDATE settings SET value = 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopq
    rstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz' WHERE name = 'ABC
    DEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDE
    FGHIJKLMNOPQRSTUVWXYZ'

  5. #5
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    Précédemment dans ton programme, as-tu alloué 1*sizeof(char) de plus à tes chaînes pour placer '\0' ?
    Pour débugger rien ne vaut de compiler avec -g puis appel à gdb (ou au débuggueur de ton choix).

  6. #6
    Membre averti
    Inscrit en
    Janvier 2004
    Messages
    25
    Détails du profil
    Informations forums :
    Inscription : Janvier 2004
    Messages : 25
    Par défaut
    Le '50' Il vient de quoi ?
    C'est juste "plus" que le total de caractères que je rajoute ("UPDATE settings SET value = ", etc ...) . Je pense qu'il serait mieux que je compte au plus juste (strlen est la pour ca) mais je débute

    Deux macros :

    #define LIM_STR(s) (s)[sizeof (s) - 1]=0
    #define CHK_STR(s) assert((s)[sizeof (s) - 1]==0)
    euh ... ?? Tu pourrais expliquer un peu ? (il y a un peu trop de niveau de parenthèses pour moi

    assert, c'est quelque chose imprimé juste pour le débogage, c'est ca ?


    Apparament, il n'y a pas de problème de débordement. malloc() fonctionne comme on s'y attend, et on obtient
    Code:
    query : UPDATE settings SET value = '123' WHERE name = '45678'

    C'était quoi ton problème déjà ?
    Mon problème est juste lorsque paramValues[1] contient 12 ou 13, 28 ou 29 caractères .. à ce moment-là, après le query = malloc(tout ca), j'ai le caractère 13 (ou 29) qui est rajouté (si la chaine faite 12 ou 28 carac) ou modifié (si la chaine fait 13 ou 29).
    Je ne vois pas pourquoi malloc va modifier ma chaine, surtout si je ne lui passe même pas cette chaine en paramètres (c'était ce que j'ai voulu démontrer tout a l'heure avec mon code foireux) : je fais un malloc "dans le vent", sans même récupérer le pointeur vers la zone allouée, et pourtant il modifie quand même la chaine paramValues[1].

    Remarque : je n'ai pas essayé d'augmenter la taille de paramValues[0] ?

    Voila. Il ne peut pas y avoir de débordement de tampon (le tampon faisant au moins 20 caractères de plus que ce que je lui mets dedans) .... Et pourtant !

    Pour info, je rajoute le code de la fonction qui génére paramValues, des fois que j'aurais fait une bêtise bien avant ....

    C'est du ncurses, les chaines de field font 25 caractères de long, c'est pour ca que je coupe (donnée utile + des espaces jusqu'a 25 caractères)

    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
    int commit_changes(FIELD **field, int n, PGconn * conn) {
    	char * paramValues[2];
    	char * my_str, * my_str2;
    	char temp_buf[254];
    	int i, res;
    	for(i = 0; i < n; ++i) { 
    		my_str = strchr(field_buffer(field[i], 1), 32);
    		my_str2 = strchr(field_buffer(field[i], 0), 32);
    		*my_str = 0;
    		*my_str2 = 0;
    		if ((paramValues[0] = malloc(strlen(field_buffer(field[i], 0) +1))) != NULL && (paramValues[1] = malloc(strlen(field_buffer(field[i], 1) +1))) != NULL) {
    			strcpy(paramValues[0], field_buffer(field[i], 0));
    			strcpy(paramValues[1], field_buffer(field[i], 1));
    			res = Query_update_settings(conn, (const char **) paramValues);
    		}
    	}
    	refresh();
    }
    Merci beaucoup pour ton aide !

  7. #7
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    if ((paramValues[0] = malloc(
    [b]strlen(field_buffer(field[i], 0) +1)[/b] /* Oh ! */
    )) != NULL && (paramValues[1] = malloc(
    [b]strlen(field_buffer(field[i], 1) +1)[/b] /* Oooh ! */
    )) != NULL) {
    Sors les +1 des appels à strlen() et ça devrait rouler

    N'oublie pas les appels à free() à chaque fin de boucle (ou utilise realloc(), plus compliqué), tu as quelques fuites.

  8. #8
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par jems
    Deux macros :
    #define LIM_STR(s) (s)[sizeof (s) - 1]=0
    #define CHK_STR(s) assert((s)[sizeof (s) - 1]==0)
    Et en plus, c'est pas les bonnes:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    #define LIM_PTR(p,l) (p)[(l) -1]=0
    #define CHK_PTR(p,l) assert((p)[(l) -1]==0)
    euh ... ?? Tu pourrais expliquer un peu ? (il y a un peu trop de niveau de parenthèses pour moi

    assert, c'est quelque chose imprimé juste pour le débogage, c'est ca ?
    assert() vérifie une expression. Si elle est fausse, il arrête le programme en indiquant le nom du fichier et la ligne.
    La première macro place un 0 à la fin de la chaine (position maximale théorique).
    Le dexième vérifie que le 0 est toujors là. Si il n'y est plus, le prohramme s'arrête (en espérant qu'il n'a pas crashé avant, ce qui peut arriver). Crois-moi, ce genre de technique fait le ménage...
    Mon problème est juste lorsque paramValues[1] contient 12 ou 13, 28 ou 29 caractères .. à ce moment-là, après le query = malloc(tout ca), j'ai le caractère 13 (ou 29) qui est rajouté (si la chaine faite 12 ou 28 carac) ou modifié (si la chaine fait 13 ou 29).
    Uniquement pour ces valeurs magiques 13 et 29 ? Poste une code complet mais compilable, portable et réduit au minimum qui montre le problème.

  9. #9
    Membre averti
    Inscrit en
    Janvier 2004
    Messages
    25
    Détails du profil
    Informations forums :
    Inscription : Janvier 2004
    Messages : 25
    Par défaut
    Sors les +1 des appels à strlen() et ça devrait rouler :Wink:

    N'oublie pas les appels à free() à chaque fin de boucle (ou utilise realloc(), plus compliqué), tu as quelques fuites.
    Tout a fait Matt !

    En fait, je m'étais juste mélanger dans les niveaux de parenthèses .... Ceci dit, c'est bizarre que pour ces valeurs magiques 13, 29, 60 ca posait problème, et pas pour le reste .... enfin, c'est une bonne grosse erreur de débutant ! ;-)

    Merci pour votre aide !

  10. #10
    Membre éprouvé Avatar de kaisse
    Profil pro
    Inscrit en
    Novembre 2003
    Messages
    100
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2003
    Messages : 100
    Par défaut
    Ceci dit, c'est bizarre que pour ces valeurs magiques 13, 29, 60 ca posait problème, et pas pour le reste
    Typiquement, ton programme s'amusait à écrire un caractère ou deux en dehors de la chaîne allouée. A partir de là, le comportement devient indéfini, et est grandement dépendant de l'implémentation. Donc, il peut se passer n'importe quoi (même rien, ou quelque chose qui ne géne pas l'exécution de ton programme). Celà dit, ce que tu as observé, est surement une conséquence de l'implémentation de malloc et de free sur ton système.

  11. #11
    Membre averti
    Inscrit en
    Janvier 2004
    Messages
    25
    Détails du profil
    Informations forums :
    Inscription : Janvier 2004
    Messages : 25
    Par défaut
    Typiquement, ton programme s'amusait à écrire un caractère ou deux en dehors de la chaîne allouée. A partir de là, le comportement devient indéfini, et est grandement dépendant de l'implémentation. Donc, il peut se passer n'importe quoi (même rien, ou quelque chose qui ne géne pas l'exécution de ton programme). Celà dit, ce que tu as observé, est surement une conséquence de l'implémentation de malloc et de free sur ton système.
    Oui, bien sur ... c'est juste que je trouvais rigolo le fait que ce soit régulier (toujours le même problème) au même endroit (quand 13 caractères, 28 , etc ...) et bien après la première allocation ...

    Ca me laisse a penser que dans de grands projets, il doit y avoir des erreurs qui passent inapercu pendant longtemps, et quand on s'apercoit qu'il y a un problème, ca doit certainement être long a résoudre, si on ne regarde pas au bon endroit ...

    Enfin, voila, ce topic est clos, merci beaucoup pour votre aide, et a bientot !

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

Discussions similaires

  1. Comportement plus qu'étrange du malloc
    Par mkrzemin dans le forum C
    Réponses: 12
    Dernier message: 06/02/2011, 21h55
  2. Comportement de malloc/realloc
    Par gege2061 dans le forum C
    Réponses: 5
    Dernier message: 08/12/2005, 15h09
  3. Pb : malloc qui marche une fois sur deux .... ?
    Par guillaume_pfr dans le forum C
    Réponses: 14
    Dernier message: 21/07/2003, 09h52
  4. Erreur de sgmentation avec malloc
    Par simonm dans le forum C
    Réponses: 5
    Dernier message: 27/02/2003, 08h29
  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