[Résolu] Vous avez dit Memcpy() ?!

Vous avez dit Memcpy() ?! [Résolu] - C - Programmation

Marsh Posté le 03-12-2008 à 10:04:58    

Bonjour à tous,
 
Voilà je suis actuellement sur un projet d'écriture de tests unitaires en C.
 
J'utilise la librairie classique de CUnit, libcunit.so.
 
Mais lorsque mon code en arrive à la phase suivante, il segfault direct.
 

Code :
  1. void test_dstrcat(void)
  2. {
  3. char *s1 = "String1";
  4. DynString DStr = dstr(7, 0);
  5. DStr->ptr = "String2";
  6. int i = dstrcat(DStr, s1);
  7. CU_ASSERT_EQUAL(i, 0);
  8. }


 
Ca se passe à la ligne intitulée :

Code :
  1. int i = dstrcat(DStr, s1);


 
Apparemment, dstrcat() fait appel à la fonction memcpy() de la libcunit.
 
Je fourni donc la fonction dstrcat() :
 

Code :
  1. int dstrcat( DynString_t *s, char *s1 )
  2. {
  3. int l = strlen(s1) ;
  4. _last_l = l ;
  5. if( ! l ) return(1) ;
  6. CheckAlloced( s, l ) ;
  7. memcpy( s->ptr+s->length, s1, l ) ;
  8. s->ptr[s->length+l] = 0 ;
  9. s->length += l ;
  10. return( 1 ) ;
  11. }


 
et même la structure DynString() :
 

Code :
  1. typedef struct _DynString
  2. {
  3. char *ptr ;
  4. int length ;
  5. int alloced ;
  6. int grain ;
  7. }
  8. DynString_t ;
  9. typedef struct _DynString *DynString ;


 
EDIT : Je vous fourni aussi le résumé du debug via GDB, qui sait, ça pourra peut être servir.
 

Code :
  1. GNU gdb 6.8-debian
  2. Copyright (C) 2008 Free Software Foundation, Inc.
  3. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
  4. This is free software: you are free to change and redistribute it.
  5. There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
  6. and "show warranty" for details.
  7. This GDB was configured as "i486-linux-gnu"...
  8. (gdb) run
  9. Starting program: /home/vincent/MOULIN/TESTSCUNIT/string
  10.      CUnit - A Unit testing framework for C - Version 2.1-0
  11.      http://cunit.sourceforge.net/
  12. Program received signal SIGSEGV, Segmentation fault.
  13. 0xb7df09b5 in memcpy () from /lib/tls/i686/cmov/libc.so.6
  14. (gdb) where
  15. #0  0xb7df09b5 in memcpy () from /lib/tls/i686/cmov/libc.so.6
  16. #1  0x0804de48 in dstrcat ()
  17. #2  0x08049466 in test_dstrcat () at tests_string.c:62
  18. #3  0xb7ecf281 in ?? () from /usr/lib/libcunit.so.1
  19. #4  0xb7ecf7d9 in ?? () from /usr/lib/libcunit.so.1
  20. #5  0xb7ecfa30 in CU_run_all_tests () from /usr/lib/libcunit.so.1
  21. #6  0xb7ed1693 in CU_basic_run_tests () from /usr/lib/libcunit.so.1
  22. #7  0x0804b587 in main () at tests_string.c:772


 
Si quelqu'un aurait une idée, je me bat depuis hier matin et j'ai pas l'impression d'avoir fait une erreur monumentale.
 
Merci d'avance ;)
 
Vincent.


Message édité par MindKiller67 le 03-12-2008 à 16:24:54
Reply

Marsh Posté le 03-12-2008 à 10:04:58   

Reply

Marsh Posté le 03-12-2008 à 10:40:33    

Que fait la fonction dstr(7, 0) ?
J'ai un peu l'impression qu'il te manque le zéro terminal des chaînes (tu fais après un DStr->ptr = "String2"; et "String2" fait juste 7 caractères !
_last_l est une variable globale ? peut-être à éviter non ?

Reply

Marsh Posté le 03-12-2008 à 10:46:34    

La fonction dstr(7, 0) créé un DynString de longueur 7, effectivement.
 
Je crois voir qu'il manque effectivement un espace mémoire pour le zéro terminal.
 
D'où ce foutu segfault je pense ... le copier/coller est définitivement à maudir !
 
Je m'en vais essayer.
 
Le projet que je teste fait plus de 2 millions de lignes de code, comprenez un peu le problème, j'ai du mal à tout garder en tête.
Je sais pas comment mon directeur R&D s'y prend, moi j'ai du mal :p
 
Merci :)
 
Vincent.


Message édité par MindKiller67 le 03-12-2008 à 10:57:21
Reply

Marsh Posté le 03-12-2008 à 10:55:56    

Me revoilà, j'ai modifié la fonction dstr(), et la voilà :
 

Code :
  1. DynString_t *dstr( int size, int grain )
  2. {
  3. DynString_t *s = calloc( 1, sizeof(DynString_t) ) ;
  4. s->ptr = malloc( size+1 ) ;
  5. s->ptr[0] = 0 ;
  6. s->length = 0 ;
  7. s->alloced = size ;
  8. s->grain = grain ;
  9. return( s ) ;
  10. }


 
Du coup, elle créé un DynString de longueur 7 et alloue un espace mémoire de 8.
 
Et la variable "_last_l" est déclarée en "static int" dans le source de dstr().
 
Mais toujours est-il que j'ai encore le même problème.
 
Une erreur de segmentation au même endroit.
 
Je creuse, mais ça devient un dialogue de sourd entre moi et ma machine.
 
Merci pour les réponses qui suivront éventuellement ;)
 
Vincent.

Message cité 1 fois
Message édité par MindKiller67 le 03-12-2008 à 10:58:47
Reply

Marsh Posté le 03-12-2008 à 10:56:19    

Bon courage, effectivement !

Reply

Marsh Posté le 03-12-2008 à 14:02:16    

MindKiller67 a écrit :

Me revoilà, j'ai modifié la fonction dstr(), et la voilà :

 
Code :
  1. DynString_t *dstr( int size, int grain )
  2. {
  3. DynString_t *s = calloc( 1, sizeof(DynString_t) ) ;
  4. s->ptr = malloc( size+1 ) ;
  5. s->ptr[0] = 0 ;
  6. s->length = 0 ;  // hem, c'est sur qu'il y a pb la
  7. s->alloced = size ; // et peut etre ici aussi
  8. s->grain = grain ;
  9. return( s ) ;
  10. }
 

Du coup, elle créé un DynString de longueur 7 et alloue un espace mémoire de 8.

 

Et la variable "_last_l" est déclarée en "static int" dans le source de dstr().

 

Mais toujours est-il que j'ai encore le même problème.

 

Une erreur de segmentation au même endroit.

 

Je creuse, mais ça devient un dialogue de sourd entre moi et ma machine.

 

Merci pour les réponses qui suivront éventuellement ;)

 

Vincent.

C'est clair que tu as un probleme avec une telle definition.
s->length = size ;
s->alloced = size+1 ; (ou size? Mais savoir ce que fait CheckAlloced serait aussi utile ici pour savoir ce qu'il faut mettre comme valeur dans s->alloced)
me semblerait plus juste...
Notes que tu peux garder s->length = 0 ; si tu veux mais alors, apres la ligne
DStr->ptr = "String2";
il faut faire
DStr->length = size ;

 

A+,


Message édité par gilou le 03-12-2008 à 14:11:32

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 03-12-2008 à 14:14:50    

j'ai l'impression que dstrcat(DStr, s1); sert a concaténer DStr et s1, le tout dans DStr->ptr ? Du coup il faut penser à allouer (avec dstr()) suffisamment de mémoire pour accuillir le nombre de caractères égale à la somme des deux:
DynString DStr;
char *s1="String1", *s2="String2";
DStr = dstr(strlen(s1)+strlen(s2), 0);
DStr->ptr = s2;

Reply

Marsh Posté le 03-12-2008 à 14:22:19    

Oui, je viens de reagir en relisant son code:

Citation :

DStr->ptr = "String2";


c'est censé marcher??  :heink:  
le = ca ne vas pas faire une copie d'un buffer dans un autre.
 
Il va falloir d'abord correctement faire les choses. (strcpy...)
djobidjoba, tu as aussi vu le pb puisque tu as ecrit:

Citation :

DStr->ptr = s2;

 
mais je pense qu'il faut faire une copie, car il y a le risque de modifier effectivement s2, par effet de bord. Ce qui n'est peut être pas voulu

Citation :

il faut penser à allouer (avec dstr()) suffisamment de mémoire pour accuillir le nombre de caractères égale à la somme des deux:

A priori, je pense que c'est ce que fait checkAlloced dans son dstrcat, c'est pourquoi je demandais ca dans mon post precedent.
A+,


Message édité par gilou le 03-12-2008 à 14:24:57

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 03-12-2008 à 14:37:49    

Je te montre le code de la fonction CheckAlloced, mais y'a rien de mystérieux.
 

Code :
  1. static void CheckAlloced( DynString_t *s, int l )
  2. {
  3. if( s->length+l > s->alloced )
  4. {
  5.  int ll = MAX( s->alloced+s->grain, s->length+l ) ;
  6.  char *nptr = realloc( s->ptr, ll+1 ) ;
  7.  if( ! nptr ) return ;
  8.  s->ptr = nptr ;
  9.  s->alloced = ll ;
  10. }
  11. }


 
En fait, elle ne fait que réallouer la mémoire et placer alloced à sa valeur juste.
 
dstr() ne fait qu'une allocation, sans s'occuper de la taille, mais dstrcat() lui remet les pendules à l'heure en appelant CheckAlloced().
 
J'ai débuggé et il se trouve que "alloced" prend la valeur du multiple de "grain" directement supérieur à "size".
 
Après les opérations, tout va bien. Les valeurs sont correctes.
 
Mais ca plante à :
 

Code :
  1. memcpy( s->ptr+s->length, s1, l );


 
Et non, je ne peux pas modifier le code en remplaçant mon CheckAlloced() par autre chose.

Reply

Marsh Posté le 03-12-2008 à 14:41:00    

Ici le = c'est une copie de pointeur et c'est valable uniquement dans la fonction en cours.

Reply

Marsh Posté le 03-12-2008 à 14:41:00   

Reply

Marsh Posté le 03-12-2008 à 14:47:11    

djobidjoba a écrit :

Ici le = c'est une copie de pointeur et c'est valable uniquement dans la fonction en cours.

Ce qui rend caduc le malloc du buffer de Dstr...
Donc manifestement, c'est une copie qui est recherchée.
A+,


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 03-12-2008 à 14:55:11    

dstr() est utilisée dans la fonction en cours mais c'est clair qu'un bon vieux str/memcpy c'est plus sûr.
là où ca devient incorrect c'est si test_dstrcat retourne DStr, mais le compilateur envois un warning là.


Message édité par djobidjoba le 03-12-2008 à 14:56:35
Reply

Marsh Posté le 03-12-2008 à 15:00:40    

MindKiller67 a écrit :

Et non, je ne peux pas modifier le code en remplaçant mon CheckAlloced() par autre chose.

Non non, c'était juste pour être sur que ca faisait bien de la reallocation (et pas un simple check). Aucune raison de modifier cette fonction.
Si tu remplaces

Citation :

DynString DStr = dstr(7, 0);
DStr->ptr = "String2";


par
char* s2 = "String2":
DynString DStr = dstr(strlen(s2)+1, 0);  (ou DynString DStr = dstr(strlen(s2), 0);  avec ton dstr modifié)
strcpy(DStr->ptr, s2);
DStr->length = strlen(s2);
ca devrait coller, mais il serait plus propre de faire une fonction
DynString_t *make_dstr(char *s) {
  int l = strlen(s);
  DynString_t *ds = dstr(l+1, 0);  // ou dstr(l, 0); si ton dstr fait une allocation de l+1 pour le buffer
  strcpy(ds->ptr, s);
  ds->length = l;
  return ds;
}
et tu appelles ca dans ton code avec
DynString DStr = make_dstr("String2" );
A+,


Message édité par gilou le 03-12-2008 à 15:25:28

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 03-12-2008 à 15:06:17    

Ok bien compris, j'vais faire une petite fonction à côté ... mais ca me plait pas trop, j'ai pas le droit dans la logique :)
 
Effectivement ça fonctionne pas non plus en mettant "DStr->length" à 7.
 
Pour ce qui est de test_dstrcat(), c'est une fonction de test qui ne renverra strictement rien. Elles sont appelées et fournissent des infos via le CU_ASSERT() pour afficher le résultat du test, c'est tout.
 
Par contre je vois pas ce que tu veux dire par "Donc manifestement, c'est une copie qui est recherchée.", m'sieur Gilou :)
Lors de l'écriture de tests, on doit utiliser l'API telle qu'elle est ^^
 
Je crois voir le bout du tunnel au loin, une lumière blanche et aveuglante !
Merci.
 
Vincent.


Message édité par MindKiller67 le 03-12-2008 à 15:11:16
Reply

Marsh Posté le 03-12-2008 à 15:24:43    

Citation :

Par contre je vois pas ce que tu veux dire par "Donc manifestement, c'est une copie qui est recherchée.", m'sieur Gilou


Quand tu fais:
DynString DStr = dstr(7, 0);  
Dstr->ptr pointe sur le buffer que tu as alloué en faisant s->ptr = malloc( size+1 ) ; dans dstr
quand apres tu fais:
Dstr->ptr = "String2"
Tu perds ce que tu avais alloué, car tu écrases l'adresse de la zone allouée par celle de la chaine "String2" (qui d'ailleurs est dans une toute autre zone memoire)
-----------------------------------------------------------------------------------------------
Et incidemment, Dstr->length pointe toujours sur 0 a priori (si tu as fait, s->length = 0 dans dstr), ce qui va faire de gros problemes dans dstrcat.
Comme tu as Dstr->length = 0, et que String1 et String2 de meme taille, checkAlloced va trouver qu'il y a assez de place, ne va rien allouer, et dstrcat va essayer d'ecrire la chaine concatenée a la position s->ptr+s->length = s->ptr a la ligne memcpy( s->ptr+s->length, s1, l ) ;
Donc dstrcat est en train d'essayer d'écrire "String1" a l'emplacement ou il y a "String2", chaine constante, donc zone protégée en écriture.
kaboing...
Capice?
-----------------------------------------------------------------------------------------------
Donc plutot que d'ecraser l'adresse de la zone allouée, tu gardes ton buffer, et tu ne fais pas Dstr->ptr = "String2".
Tu remplis ton buffer en faisant une copie strcpy(Dstr->ptr, "String2" ) et tu ajuste la taille de ta chaine dynamique Dstr->length = strlen( "String2" )
A+,


Message édité par gilou le 03-12-2008 à 15:30:52

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 03-12-2008 à 15:25:39    

Ca fonctionne, merci en tout cas pour m'avoir aidé :)
 
Par contre, si jamais vous avez une solution alternative, afin de ne pas créer de fonction annexe, je suis preneur.
 
Mon code passera pas la validation avec ça :
 

Code :
  1. DynString_t *make_dstr(char *s)
  2. {
  3. int l = strlen(s);
  4. DynString_t *DStr = dstr(l, 0);
  5. strcpy(DStr->ptr, s);
  6. /* DStr->length = l; # Inutile car récupéré par l'API on ne sait comment ! */
  7. return DStr;
  8. }


 
Doit y avoir un moyen d'initialiser ce foutu DynString autrement, j'vais y bosser perso, que mon code soit valable :)
 
En tout cas vraiment un grand merci pour l'idée de la petite fonction. Ca me permet de débugger le reste de l'application en attendant de trouver.

Reply

Marsh Posté le 03-12-2008 à 15:47:24    

Bon, au vu de ces troislignes de CheckAlloced,  

Citation :

int ll = MAX( s->alloced+s->grain, s->length+l ) ;
char *nptr = realloc( s->ptr, ll+1 ) ;
........
s->alloced = ll ;


on comprends que le champ alloced fait 1 de moins que ce qui est reellement alloué
Donc ta fonction *dstr doit être comme suit:

Code :
  1. DynString_t *dstr( int size, int grain )
  2. {
  3. int n = MAX(size, grain);
  4. DynString_t *s = calloc( 1, sizeof(DynString_t) ) ;
  5. s->ptr = malloc( n+1 ) ;
  6. s->ptr[0] = 0 ;
  7. s->length = 0 ;
  8. s->alloced = n ;
  9. s->grain = grain ;
  10. return( s ) ;
  11. }


 
avec ça, ton code initial devient:

Code :
  1. void test_dstrcat(void)
  2. {
  3. char *s1 = "String1";
  4. DynString DStr = dstr(7, 0);
  5. strcpy(DStr->ptr, "String2" );
  6. DStr->length = strlen("String2" );
  7. int i = dstrcat(DStr, s1);
  8. CU_ASSERT_EQUAL(i, 0);
  9. }


 
Ce qui ne fais donc pas de grosse modif a ton code initial.
 
Un code un poil plus secure serait de faire:

Code :
  1. void test_dstrcat(void)
  2. {
  3. char *s1 = "String1";
  4. DynString DStr = dstr(strlen("String2" ), 0);
  5. strncat(DStr->ptr, "String2", DStr->alloced);
  6. DStr->length = strlen(DStr->ptr);
  7. int i = dstrcat(DStr, s1);
  8. CU_ASSERT_EQUAL(i, 0);
  9. }


 
A+,


Message édité par gilou le 03-12-2008 à 16:00:29

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 03-12-2008 à 16:23:21    

Ben à la limite, j'peux utiliser dstrcat :-°
 
Aucune idée de pourquoi je n'y ai pas pensé avant.
 
Mon code deviendrait :

Code :
  1. void test_dstrcat(void)
  2. {
  3.     char *s1 = "String1";
  4.     char *s2 = "String2";
  5.     DynString DStr1 = dstr(7, 0);
  6.     int a = dstrcat(DStr1, s1)
  7.     int i = dstrcat(DStr1, s2);
  8.     CU_ASSERT_EQUAL(i, 0);
  9. }


 
Ce qui ferait que :
 
J'initialise deux chaînes de caractère.
Je concatène une chaîne de base (s1) à mon DynString vide.
    - J'obtiens donc un DynString avec la valeur de la première chaîne (s1).
Je concatène la deuxième chaîne (s2) au DynString construit ci-dessus.
    - J'obtiens donc le même DynString avec DStr->ptr = "String1String2".
 
Fin de l'histoire ... :-)
 
C'est ton :

Code :
  1. strncat(DStr->ptr, "String2", DStr->alloced);

qui m'y a fait penser !
 
Merci à tous, à bientôt.


Message édité par MindKiller67 le 03-12-2008 à 16:26:24
Reply

Sujets relatifs:

Leave a Replay

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