logo Debian Debian Debian-France Debian-Facile Debian-fr.org Forum-Debian.fr Debian ? Communautés logo inclusivité

Debian-facile

Bienvenue sur Debian-Facile, site d'aide pour les nouveaux utilisateurs de Debian.

Vous n'êtes pas identifié(e).

#1 15-02-2020 04:15:19

Clem
Membre
Lieu : Au coin du bois
Distrib. : Stable
Noyau : 4.9.0-4 *-amd-64
(G)UI : XMonad & Mate
Inscription : 09-03-2010

{C} concatenation a la chaine

Bonjour a tous

Je dois etre miro. J'ai une erreur sur un code tout simple:


char* struct_to_str (GSList* G)
{
    p_maille A = g_slist_nth_data(G,0);
    char* chaine = malloc(sizeof (A->justification+1));
    strcpy (chaine, A->justification);
    strcat(chaine, "\n");
    G=g_slist_next(G);

    while (G)
    {
  char *tmp;
  int i,j;
  p_maille A = g_slist_nth_data(G,0);

  j= strlen (chaine);
  i= strlen (A->justification);
  tmp = malloc(sizeof(chaine));
 
  strcpy(tmp,chaine);
  free(chaine);

 
  chaine = malloc(sizeof(i+j+1));
 
  strcpy(chaine,tmp);
  strcat(chaine, A->justification);
  strcat(chaine, "\n");

  free(tmp);

  G=g_slist_next(G);
    }

    return chaine;
}
 



L'erreur :


free(): invalid size
Abandon
 



Je n'arrive pas a deviner ce qui ne marche pas. Les variables m'ont  l'air correctement allouées.

Dernière modification par Clem (15-02-2020 04:21:04)


Moi, je suis PC (x86_64) et formater windows, c'était MON idée
Le sommeil de la raison ...

Hors ligne

#2 15-02-2020 10:51:09

raleur
Membre
Inscription : 03-10-2014

Re : {C} concatenation a la chaine

Clem a écrit :

char* chaine = malloc(sizeof (A->justification+1));
...
tmp = malloc(sizeof(chaine));
...
chaine = malloc(sizeof(i+j+1));


"chaine" est un pointeur et non un tableau, donc sizeof(chaine) renvoie la taille d'un pointeur et non la taille de la mémoire allouée par le premier malloc().
Quant à sizeof(i+j+1), ça n'a pas de sens. Le résultat est la taille d'un int alors que je suppose que tu veux allouer une taille égale à la somme.

Dernière modification par raleur (15-02-2020 10:57:28)


Il vaut mieux montrer que raconter.

Hors ligne

#3 15-02-2020 18:50:01

Clem
Membre
Lieu : Au coin du bois
Distrib. : Stable
Noyau : 4.9.0-4 *-amd-64
(G)UI : XMonad & Mate
Inscription : 09-03-2010

Re : {C} concatenation a la chaine

Ho, la bourde.
Merci c'est rectifié.


char* chaine = malloc(sizeof (char * (i+1)));
 

etc...

maintenant le compilateur me renvoit des erreurs que je ne comprends pas:


 gcc -W -Werror -Wall -ansi `pkg-config --cflags --libs gtk+-3.0` ~/Code/C/Essai/Essai.c -o ~/Code/C/Essai/Essai -std=c11
/home/clem/Code/C/Essai/Essai.c: In function ‘struct_to_str’:
/home/clem/Code/C/Essai/Essai.c:234:43: error: expected ‘)’ before ‘i’
     char* chaine = malloc(sizeof (char * (i+1)));
                                           ^
                                           )
/home/clem/Code/C/Essai/Essai.c:247:28: error: expected ‘)’ before ‘j’
  tmp = malloc(sizeof(char*(j)));
                            ^
                            )
/home/clem/Code/C/Essai/Essai.c:253:32: error: expected ‘)’ before ‘i’
  chaine = malloc(sizeof(char *(i+j+1)));
                                ^
                                )
/home/clem/Code/C/Essai/Essai.c:242:8: error: variable ‘j’ set but not used [-Werror=unused-but-set-variable]
  int i,j;
        ^
/home/clem/Code/C/Essai/Essai.c:242:6: error: variable ‘i’ set but not used [-Werror=unused-but-set-variable]
  int i,j;
      ^
/home/clem/Code/C/Essai/Essai.c:233:9: error: unused variable ‘i’ [-Werror=unused-variable]
     int i = strlen (A->justification);
         ^
cc1: all warnings being treated as errors

 

Dernière modification par Clem (15-02-2020 18:51:39)


Moi, je suis PC (x86_64) et formater windows, c'était MON idée
Le sommeil de la raison ...

Hors ligne

#4 15-02-2020 19:56:05

raleur
Membre
Inscription : 03-10-2014

Re : {C} concatenation a la chaine

sizeof (char * (i+1))


est incorrect. sizeof attend soit un type, soit le résultat d'une expression pour déterminer sa taille. Or

char * (i+1)


n'est ni un type, ni une expression valide. Ça ne veut rien dire.
Que veux-tu obtenir exactement ? Si tu veux la taille d'un tableau de (i+j) caractères, tu peux écrire

sizeof(char [i+1])


ou plus simplement

sizeof(char)*(i+1)


Il vaut mieux montrer que raconter.

Hors ligne

#5 16-02-2020 10:40:30

Clem
Membre
Lieu : Au coin du bois
Distrib. : Stable
Noyau : 4.9.0-4 *-amd-64
(G)UI : XMonad & Mate
Inscription : 09-03-2010

Re : {C} concatenation a la chaine

raleur a écrit :

Que veux-tu obtenir exactement ? Si tu veux la taille d'un tableau de (i+j) caractères,


Oui ç'est ça.


 char* chaine = malloc(sizeof (char) * (i+1));
[...}
tmp = malloc(sizeof(char)* (j));
[...]
chaine = malloc(sizeof(char) * (i+j+1));
 


Là, le compilateur ne me renvoit plus d'erreur.
Je ne suis pas sur d'avoir compris toute la subtilité de la chose...

Par contre a l'execution j'ai le droit à :


realloc(): invalid next size
Abandon
 



L'erreur se situe en dehors de la fonction j'ai l'impresssion:


char *justification = struct_to_str(G);

  printf("justification : %s\n", justification);

  lbl=gtk_label_new("premier \nsecond \ntertiaire \n");
  gtk_label_set_text (GTK_LABEL (lbl), justification);
  gtk_container_add(GTK_CONTAINER(frame), lbl);

 


Moi, je suis PC (x86_64) et formater windows, c'était MON idée
Le sommeil de la raison ...

Hors ligne

#6 16-02-2020 10:59:54

raleur
Membre
Inscription : 03-10-2014

Re : {C} concatenation a la chaine

En tout cas je ne vois pas d'appel à realloc() dans ta fonction. Mais si ça se trouve, malloc() fait juste un appel à realloc() avec ptr=NULL (et free() fait de même avec size=0).

Peux-tu reposter le code complet de ta fonction avec toutes les modifications apportées ?

Dernière modification par raleur (16-02-2020 11:05:54)


Il vaut mieux montrer que raconter.

Hors ligne

#7 16-02-2020 11:10:23

Clem
Membre
Lieu : Au coin du bois
Distrib. : Stable
Noyau : 4.9.0-4 *-amd-64
(G)UI : XMonad & Mate
Inscription : 09-03-2010

Re : {C} concatenation a la chaine

Hop:


char* struct_to_str_un (GSList* G)
{
    p_maille A = g_slist_nth_data(G,0);
    int i = strlen (A->justification);
    char* chaine = malloc(sizeof (char) * (i+1));
    strcpy (chaine, A->justification);
    strcat(chaine, "\n");
    G=g_slist_next(G);

    while (G)
    {
  char *tmp;
  int i,j;
  p_maille A = g_slist_nth_data(G,0);

  j= strlen (chaine);
  i= strlen (A->justification);
  tmp = malloc(sizeof(char)* (j));
 
  strcpy(tmp,chaine);
  free(chaine);

 
  chaine = malloc(sizeof(char) * (i+j+1));
 
  strcpy(chaine,tmp);
  strcat(chaine, A->justification);
  strcat(chaine, "\n");

  free(tmp);

  G=g_slist_next(G);
    }

    return chaine;
}

 

Dernière modification par Clem (16-02-2020 11:10:41)


Moi, je suis PC (x86_64) et formater windows, c'était MON idée
Le sommeil de la raison ...

Hors ligne

#8 16-02-2020 11:52:43

raleur
Membre
Inscription : 03-10-2014

Re : {C} concatenation a la chaine

Attention, la taille retournée par strlen() n'inclut pas le caractère nul final. Il faut en tenir compte dans les tailles allouées par malloc().

PS : à titre personnel, je trouve bizarre et risqué de redéfinir les mêmes noms de variables A et i dans la boucle while().
Il me semble que ce serait plus efficace de calculer et maintenir la taille de la chaîne dans une variable plutôt que de la réévaluer avec strlen() à chaque itération.
Tu fais au moins une allocation et une copie inutile : chaine -> tmp puis tmp -> chaine alors qu'il suffirait d'allouer directement la nouvelle taille.
D'ailleurs, pourquoi ne pas agrandir la zone mémoire avec realloc() plutôt que d'en allouer une nouvelle ?

Il vaut mieux montrer que raconter.

Hors ligne

#9 16-02-2020 15:20:36

raleur
Membre
Inscription : 03-10-2014

Re : {C} concatenation a la chaine

raleur a écrit :

Il me semble que ce serait plus efficace de calculer et maintenir la taille de la chaîne dans une variable plutôt que de la réévaluer avec strlen() à chaque itération.
Tu fais au moins une allocation et une copie inutile : chaine -> tmp puis tmp -> chaine alors qu'il suffirait d'allouer directement la nouvelle taille.


J'ai aussi intégré la première itération dans la boucle.

char *struct_to_str_un(GSList *G)
{
  p_maille A;
  char *chaine;
  char *tmp;
  int l;
 
  chaine = malloc(sizeof(char)); // \0 terminal
  *chaine='\0';
  l = 0;
 
  while (G)
  {
    A = g_sli t_nth_data(G, 0);
    l += strlen(A->justification)+1; // +1 pour \n
    tmp = malloc(sizeof(char)*(l+1)); // +1 pour \0 terminal
    strcpy(tmp, chaine);
    free(chaine);
    chaine = tmp;
    strcat(chaine, A->justification);
    strcat(chaine, "\n");
    G=g_slist_next(G);
  }
 
  return chaine;
}
 


raleur a écrit :

D'ailleurs, pourquoi ne pas agrandir la zone mémoire avec realloc() plutôt que d'en allouer une nouvelle ?


Au passage j'ai remplacé les strcat() par strcpy() puisque les tailles sont connues, ça évite de chercher la fin de la chaîne destination.

char *struct_to_str_un(GSList *G)
{
  p_maille A;
  char *chaine;
  int l;
  int s;
 
  chaine = malloc(sizeof(char)); // +1 pour \0 terminal
  *chaine = '\0';
  l = 0;
 
  while (G)
  {
    A = g_sli t_nth_data(G, 0);
    s = strlen(A->justification);
    chaine = realloc(chaine, sizeof(char)*(l+s+2)); // +2 pour \n et \0 terminal
    strcpy(chaine+l, A->justification);
    l += s;
    strcpy(chaine+l++, "\n");
    G = g_slist_next(G);
  }
 
  return chaine;
}
 



PS : ne pas oublier d'appeler free() avec le pointeur retourné par la fonction une fois que le contenu a été utilisé.

Dernière modification par raleur (16-02-2020 15:40:03)


Il vaut mieux montrer que raconter.

Hors ligne

#10 16-02-2020 17:39:57

Clem
Membre
Lieu : Au coin du bois
Distrib. : Stable
Noyau : 4.9.0-4 *-amd-64
(G)UI : XMonad & Mate
Inscription : 09-03-2010

Re : {C} concatenation a la chaine

Nickel ca marche big_smile

cependant je ne comprends pas cette manipulation:

strcpy(chaine+l, A->justification);
{...]
strcpy(chaine+l++, "\n");

Et la fonction strcpy n'est pas censée écraser le contenu de la chaine receptrice?

Dernière modification par Clem (16-02-2020 17:50:46)


Moi, je suis PC (x86_64) et formater windows, c'était MON idée
Le sommeil de la raison ...

Hors ligne

#11 16-02-2020 18:52:16

raleur
Membre
Inscription : 03-10-2014

Re : {C} concatenation a la chaine

Clem a écrit :

strcpy(chaine+l, A->justification);


Le résultat de l'addition d'un pointeur et d'un entier n est un pointeur qui pointe vers le n-ième élément. En C, les pointeurs et tableaux étant interchangeables dans ce genre d'expression, *(chaine+l) est équivalent à chaine[l]. Ici, chaine+l pointe vers le l-ième caractère de la chaîne pointée par chaine. Si l est la longueur de la chaîne, chaine+l pointe vers la fin de la chaîne.

Clem a écrit :

strcpy(chaine+l++, "\n");


Même principe, mais combiné avec l'opérateur de post-incrémentation ++ qui incrémente la variable l après l'avoir utilisée pour ajouter un caractère de saut de ligne à la fin. C'est équivalent à

strcpy(chaine+l, "\n");
l += 1;


Il faut faire attention lorsqu'on utilise ce type d'opérateur dans une expression un peu compliquée (ce n'est pas le cas ici), cela peut produire des résultats inattendus.

Clem a écrit :

Et la fonction strcpy n'est pas censée écraser le contenu de la chaine receptrice?


Oui, mais ici on décale le début de la chaîne destinataire à la fin de la chaîne actuelle (dont on connaît la taille, donc la position de la fin).

C'est une vieille habitude de ma part de chercher à écrire du code C efficace (sans nuire à la lisibilité) car à l'époque fort lointaine où je programmais en C, c'était pour des micro-contrôleurs qui avaient très peu de puissance et de mémoire (genre un CPU 8 bits à 8 MHz et 32 Kio de RAM, pas de coprocesseur mathématique...). La fonction strcat() doit parcourir toute la chaîne destination pour trouver où elle se termine (ou faire appel à la fonction strlen() qui fait la même chose) afin d'y copier la chaîne source, ce qui prend du temps alors qu'on peut savoir à l'avance où est cette position et y copier directement la chaîne source.

Dernière modification par raleur (16-02-2020 18:55:04)


Il vaut mieux montrer que raconter.

Hors ligne

#12 19-02-2020 13:48:04

Clem
Membre
Lieu : Au coin du bois
Distrib. : Stable
Noyau : 4.9.0-4 *-amd-64
(G)UI : XMonad & Mate
Inscription : 09-03-2010

Re : {C} concatenation a la chaine

Ok, merci our ces précisions smile

Moi, je suis PC (x86_64) et formater windows, c'était MON idée
Le sommeil de la raison ...

Hors ligne

#13 24-02-2020 09:56:47

kao
Modérateur
Distrib. : Testing
Noyau : Linux 4.quelquechose
(G)UI : Gnome 3
Inscription : 27-09-2012
Site Web

Re : {C} concatenation a la chaine

Petite précision :
La place pour stocker la chaîne de caractères est définie à l'initialisation de la variable.
Ça veut dire que si tu as prévu de faire rentrer un mot plus long, plus tard, il risque d'y avoir un débordement de mémoire.

Si tu alloues 5 caractères :

char text[5] ="Petit";



Et que tu veux y placer un chaîne plus grande tu risques d'avoir un problème

text = "GrandeChaine";


Dépasser de 1 suffit à faire un segfault je pense.

Selon le contexte il peut être intéressant de prévoir large à l'avance :

#define MAX_CARACT 255
char text[MAX_CARACT] ="petit";
text = "GrandeChaine";


Cela prend un peu plus de place, mais évitera de faire un delete puis nouveau malloc à chaque changement de taille.

A toi de voir.

Hors ligne

#14 24-02-2020 11:53:15

raleur
Membre
Inscription : 03-10-2014

Re : {C} concatenation a la chaine

Le code de Clem n'utilise pas un tableau (de taille fixe) mais une zone mémoire allouée par malloc() qui peut être redimensionnée par realloc().

Bien sûr, allouer d'un coup la taille maximum dont on a besoin à l'avance est l'idéal à condition de la connaître. Dans le cas présent, je suppose que cela impliquerait de parcourir la liste une première fois afin de récupérer la taille de toutes les lignes avant de les concaténer. Ça peut être plus efficace que l'utilisation de realloc() si cette dernière implique une copie de la zone mémoire dans un nouvel emplacement quand il n'est pas possible de l'agrandir sur place.

kao a écrit :

char text[5] ="Petit";


Marche pas. Cette chaîne occupe 6 caractères en comptant le caractère nul final.

kao a écrit :

text = "GrandeChaine";


On peut faire ça en C ? Littéralement, cette expression signifie "affecter au pointeur 'text' l'adresse de la chaîne constante", ce qui me semble incorrect car 'text' n'est pas une variable pointeur sur données constantes (const char *) mais un tableau de données variables, on ne peut donc pas lui affecter une adresse comme valeur, et encore moins l'adresse d'une chaîne constante. A moins que le compilateur traite cette affectation comme un memcpy()/strcpy(), mais ça m'étonne. Faudra que je teste.

Dernière modification par raleur (24-02-2020 14:56:25)


Il vaut mieux montrer que raconter.

Hors ligne

#15 25-02-2020 20:24:13

raleur
Membre
Inscription : 03-10-2014

Re : {C} concatenation a la chaine

Vérification faite, je confirme qu'on ne peut pas affecter directement une chaîne à un tableau :

error: assignment to expression with array type
  text = "GrandeChaine";



Par contre gcc ne proteste pas contre l'affectation de l'adresse d'une constante chaîne à un pointeur sur char non const, mais le programme segfaulte s'il essaie de modifier le contenu de la chaîne.

Dernière modification par raleur (25-02-2020 20:25:06)


Il vaut mieux montrer que raconter.

Hors ligne

#16 26-02-2020 20:23:51

kao
Modérateur
Distrib. : Testing
Noyau : Linux 4.quelquechose
(G)UI : Gnome 3
Inscription : 27-09-2012
Site Web

Re : {C} concatenation a la chaine

raleur a écrit :


kao a écrit :

text = "GrandeChaine";


On peut faire ça en C ?


Oups ! Tu as raison, on ne peut pas...
Il faut passer par strcpy : http://www.cplusplus.com/reference/cstring/strcpy/
C'était une des grosses raisons qui m'ont fait migrer vers le C++.
La gestion des chaînes de caractères est vraiment beaucoup plus simple. (+ les containers + les objets + les booléens etc... ).

Pour GTK il faut peut-être passer par leurs string a eux : https://developer.gnome.org/glib/stable … tions.html

Hors ligne

Pied de page des forums