Forum Programmation.c Structures...

Posté par (page perso) .
Tags : aucun
0
15
sept.
2004
Je suis tout newb en C, et j'aimerais comprendre comment marchent les satanées structures.

rien ne vaut un bon exemple...
--------------- début du code ----------------

struct structure {
char * a;
char * b;
char * c;
};

#include <stdio.h>

struct structure *wazaa ( )
{
struct structure *to_ret;
to_ret->a = malloc (20*sizeof(char));
to_ret->a = "aaa";
to_ret->b = malloc (20*sizeof(char));
to_ret->b = "bbb";
to_ret->c = malloc (20*sizeof(char));
to_ret->c = "ccc";
return(to_ret);
}

int print_struct ( struct structure *aa )
{
printf ("%s--%s--%s\n", aa->a, aa->b, aa->c);
return (0);
}


int main (void)
{
struct structure *test;
test->a = "a non initialise";
test->b = "b non initialise";
test->c = "c non initialise";

print_struct ( test );
test = wazaa ();
print_struct ( test );
return (0);
}

--------------- Fin du code -------------------

exécution :
----
a non initialise--b non initialise--c non initialise
Erreur de segmentation
----

mon but est d'obtenir un PAS segfault, pour comprendre où et comment allouer de la mémoire aux structures contenant des pointeurs. (car j'ai un petit projet qui bloque dessus depuis 5 jours et ça commence à me les &&##@$$ menues.

merci pour votre aide. (et même si mon projet ne se débloque pas grace à ce post, je me coucherai au moins moins con !)
  • # malloc

    Posté par (page perso) . Évalué à 1.

    Dans ton main il faut allouer de la mémoire à test :
    struct structure *test = malloc( 60 * sizeof( char ));
    au lieu de
    struct structure *test;
    • [^] # On progresse... mais c'est pas encore gagné...

      Posté par (page perso) . Évalué à 1.

      --- après modifs : ---

      int main (void)
      {
      struct structure *test = malloc (60*sizeof(char));
      test->a = "a non initialise";
      test->b = "b non initialise";
      test->c = "c non initialise";

      print_struct ( test );
      test = wazaa ();
      print_struct ( test );
      return (0);
      }

      ---------------------------

      a non initialise--b non initialise--c non initialise
      aaa--bbb--ccc
      Erreur de segmentation

      mais là, GDB m'aide pas trop :
      ---------
      Program received signal SIGSEGV, Segmentation fault.
      _dl_fini () at dl-fini.c:64
      64 dl-fini.c: Aucun fichier ou répertoire de ce type.
      in dl-fini.c
      ---------
      je n'ai aucune idée de ce qu'est ce zouf ! c quoi cette affaire de dl-fini.c ? faut recompiler la glibc ?

      essayons hello world :
      --------------------------------------
      #include <stdio.h>

      int main (void)
      {
      printf ("hello world\n");
      return (0);
      }
      --------------------------------------
      mansuetus@spontexserver:~/Prog/petits_softs/tests$ ./hello
      hello world
      mansuetus@spontexserver:~/Prog/petits_softs/tests$
      ---------------------------------------


      Je ne comprends plus rien ... mais ça, c'est pas nouveau !
      • [^] # Re: On progresse... mais c'est pas encore gagné...

        Posté par (page perso) . Évalué à 2.

        chezmoicamarche.

        version de gcc ? chez moi 3.4.2

        void wazaa ( struct structure *sss )
        {
        sss->a = "aaa";
        sss->b = "bbb";
        sss->c = "ccc";
        }

        int main ( void )
        {

        struct structure *test;
        test = malloc ( sizeof ( struct structure ) );
        test->a = malloc ( 20 * sizeof ( char ) );
        test->b = malloc ( 20 * sizeof ( char ) );
        test->c = malloc ( 20 * sizeof ( char ) );

        test->a = "a non initialise";

        test->b = "b non initialise";

        test->c = "c non initialise";

        print_struct ( test );

        wazaa ( test );

        print_struct ( test );

        return ( 0 );

        }
        • [^] # Re: On progresse... mais c'est pas encore gagné...

          Posté par (page perso) . Évalué à 2.

          coup de bol

          Je persiste à dire que
          sss->a = "aaa";
          sss->b = "bbb";
          sss->c = "ccc";

          Fait pointer sss->a sur la pile, si la pile reste tout va bien si le programme la modifie à cette endroit rien ne va plus.

          En gros si tu appelle 2 ou 3 fonction entre wazaa() et print_struct(), ton programme ne marchera plus.
      • [^] # Re: On progresse... mais c'est pas encore gagné...

        Posté par (page perso) . Évalué à 0.

        EDIT OULA !!

        en fait, ça marche, je me suis gourré en modifiant...

        merci !
  • # Petite erreur

    Posté par (page perso) . Évalué à 2.

    la structure que tu renvoie n'a pas de mémoire allouée : tu déclares un pointeur et tu l'utilise sans lui allouer de mémoire.

    struct structure *wazaa ( )
    {
    struct structure *to_ret;
    to_ret = malloc(sizeof(struct structure));
    to_ret->a = malloc (20*sizeof(char));
    to_ret->a = "aaa";
    to_ret->b = malloc (20*sizeof(char));
    to_ret->b = "bbb";
    to_ret->c = malloc (20*sizeof(char));
    to_ret->c = "ccc";
    return(to_ret);
    }
    • [^] # Re: Petite erreur

      Posté par (page perso) . Évalué à 2.

      J'ai lu un peu vite.
      Comme dis plus haut , il faut que tu alloue de la mémoire pour test dans ton main :

      test = malloc(sizeof(struct structure));
      test->a = malloc (20*sizeof(char));
      test->b = malloc (20*sizeof(char));
      test->c = malloc (20*sizeof(char));

      et écris ta fonction wasaa comme ca :

      void wazaa (struct structure * sss)
      {
      sss->a = "aaa";
      sss->b = "bbb";
      sss->c = "ccc";
      }
      • [^] # Re: Petite erreur

        Posté par . Évalué à 5.

        non , toi non plus tu n'as pas compris

        ce sont des POINTEURS

        quand vous faites
        char * P = malloc (...)

        P est un pointeur ( c'est a dire une adresse) qui pointe vers une zone de la memoire que lui a aloué malloc. zone permanente tant qu'on ne fait pas free()

        quand vous faites
        P= "aaa" ;

        vous faites pointer P vers une AUTRE zone de la memoire . et dans le cas present une zone de la PILE de fonctions. quand le prog sort de wazaa , la zone de "aaa" est perdue et reutilisee pour autre chose. donc plantage garanti.

        ce qu'il faut faire c'est copier "aaa" dans la zone allouee par malloc :

        strcpy (P,"aaa")
        • [^] # Re: Petite erreur

          Posté par (page perso) . Évalué à 0.

          c'est rigolo de voir que ce sujet "tout simple" que j'ai posé de façon un peu ... brutale met pas mal de monde dans l'embarra
          • [^] # Re: Petite erreur

            Posté par . Évalué à 1.

            parcequ'on n'enseigne plus le BABA du fonctionnement d'un processeur ( pile, zone memoire, ...)

            on passe tout de suite au java , python , c++ avec des strings

            on ne sait plus comment ca marche effectivement.

            j'ai vu un tres bon programmeur venant de java , qui faisait des copies de maps en c++ pratiquement a chaque ligne ... et les maps etait prevues pour contenir plusieurs milliers d'elements ...
  • # strcpy

    Posté par . Évalué à 4.

    ha mon avis tu devrais utilise strcopy ou mieux strncopy pour affecter une chaine de caractere
    • [^] # Re: strcpy

      Posté par (page perso) . Évalué à 2.

      c'est LA bonne réponse de cette page :)

      Les autres aiderons aussi à rencontrer moins de bug mais celle te permettra de résoudre ton problème.

      Dans ta fonction wazza, tu alloue en effet de la mémoire dont tu récupére l'dresse dans ta structure. ça c'est OK, c'est la façon de procéder.
      to_ret->a = malloc (20*sizeof(char));

      Mais appès, tu écrase le pointeur par l'adresse statique d'une variable sur la pile de ta fonction. ça cé mal !
      to_ret->a = "aaa";

      A la place de ça utilise strcpy
      • [^] # "Miracle" !

        Posté par (page perso) . Évalué à 1.

        merci !

        j'avais essayé, mais j'ai du oublier d'allouer la mémoire à l'époque donc segfaults... mais là, mon prog d'origine, celui pour qui je me donne tant de mal peut enfin avancer !

        si j'avais plus de 1 d'XP je plusserais tout le thread ;-)
      • [^] # Re: strcpy

        Posté par . Évalué à 5.

        c'est plutôt UNE des bonnes réponses de cette page!

        Faire un :
        char * titi = "polom polom";
        est tout à fait légal, permis et autorisé.

        Faire un :
        char * titi = (char*) malloc (50*sizeof(char));
        strncpy (titi, "polom polom", 50);
        //éviter le strcpy!!!
        ou un :
        char * titi = strdup ("polom polom"); //presque pareil qu'au dessus
        est encore légal, permis, autorisé.

        Lequel choisir alors? Eh bien ça dépend de ce qu'on va faire de titi...
        Si titi ne va être que lu, on peut utiliser la première méthode. Si titi va être écrit, il FAUT utiliser la seconde méthode (même pour remplacer un seul caractère sans rien rajouter à la fin).

        Et en C, une chaîne entre guillemets dans le source n'est pas stocké dans la pile mais dans le segment DATA du fichier objet.

        Et enfin, faire un :
        char * titi = (char*) malloc (50*sizeof(char));
        titi = "polom polom";

        est autorisé mais fortement déconseillé. Ça ne plantera pas, mais il y aura une fuite mémoire car la valeur renvoyée par le malloc est définitivement perdue avant qu'un free() soit fait. C'est pas bien, mais bon, 99% des programmes ont des fuites mémoire et elles se comptent plus en Ko qu'en dizaines d'octets... Enfin ce n'est pas parce que la majorité des programmeurs sont des sagouins que c'est une excuse pour en être un :-)
        • [^] # Re: strcpy

          Posté par . Évalué à 1.

          > Et en C, une chaîne entre guillemets dans le source n'est pas stocké dans la pile mais dans le segment DATA du fichier objet.

          je n'ai jamais pu me faire a cette idee .

          autant je ne vois pas de pb si c'est dans le main() :
          main () {
          char * titi = "polom polom";

          }

          autant j'ai du mal a avaler que l'ecriture suivante soit ok :

          char * f() {

          char * toto = "abcd" ;

          return toto;
          }

          je prefere utiliser des
          const char * const STUFF = "toto";
          char * f() {

          char * toto = STUFF;

          return toto;
          }
          c'est strictement pareil , et generalement plus propre (centralisation des chaines) et ca me perturbe moins ...
      • [^] # Re: strcpy

        Posté par . Évalué à 2.

        Non, la bonne réponse c'aurait été d'utiliser la glib pour tout ça, et d'éviter au maximum les fonctions de la libc et tous les pbs qui peuvent se poser avec si tu fais pas gaffe...

        ma_struct = g_new0 (sizeof (struct MaStruct), 1);
        ma_struct->chaine1 = g_strdup ("cequejeveux");
        voire même
        ma_struct->chaine2 = g_strdup_printf ("valeur: %u\n", un_entier);
        etc, etc

        Ca ressemble beaucoup aux fonctions de la libc, sauf que c'est portable (ok, strdup doit être portable aussi ;), mais surtout, les fonctions sont beaucoup plus robustes à mon avis... (genre elles renvoient toujours des chaînes terminées par des \0, la plupart du temps on te renvoie une copie, ..)
  • # vive IRC

    Posté par (page perso) . Évalué à 1.

    heureux posesseur d une vraie connection internet ... va sur irc : irc.freenode.net, chan #c ... et tu aura ta reponse en moins de 1 min.

    PS : ne paste pas ton code sur un chan, met le en ligne sur un server web.
  • # Un bon bouquin pour le C

    Posté par (page perso) . Évalué à 0.

    Juste pour te préciser que pour 30¤ tu peux avoir toutes ces infos (et plein d'autres) très clairement expliquées en achetant le bouquin de Kernighan et Ritchie : Le Langage C.
    Je tiens a préciser que ce commentaire n'est pas un RTFM de plus, c'est juste un retour d'expérience : je n'ai rien compris au C jusqu'à ce que je lise ce bouquin et depuis, je m'aperçois que beaucoup de programmeurs C professionnels ou non devraient le lire avant de coder des âneries :) Bref, pour 30¤ et allez, un mois de lecture / pratique, tu n'auras plus de soucis avec le langage lui même (le C c'est vraiment très simple comme langage)
  • # ddd

    Posté par . Évalué à 1.

    Pour voir un peu ce qui se passe avec les structures (il me semble que c'est ta préoccupation initiale), utilise donc un debugger visuel.

    DDD (http://www.gnu.org/software/ddd/(...)) par exemple peut tracer pas à pas l'exécution de ton programme, préalablement compilé avec l'option -g de gcc, tout en visualisant sur un graphe le contenu de tes variables (screenshot : http://www.gnu.org/software/ddd/all.jpg(...) ; oui, je sais, c'est du C++...).

    Je pense que cette représentation devrait t'aider à comprendre et à maîtriser les structures.

    tip : pour savoir (grosso modo) si un pointeur pointe dans le segment de données ou dans la pile avec son adresse :
    0x08XXXXXX : données
    0xbffXXXXXX : pile
    ça peut aider parfois.... (e.g. pour persuader support linux que sss->a ne pointe pas sur la pile ;-) )
    (http://www.wlug.org.nz/MemoryMap(...))
    • [^] # Re: ddd

      Posté par (page perso) . Évalué à 0.

      aucune idée de ce pourquoi tu t'es fait moinsser, mais je suivrai ton conseil malgré tout : je m'aperçois que je malloc plus que je free, et d'après ce que tu sembles expliquer ddd peut le voir...

      il me sera au moins utile pour cela.

      Merci donc pour ta remarque, et je ne te plusse pas parce que je n'ai pas le droit :/
    • [^] # Re: ddd

      Posté par . Évalué à 1.

      oui en effet j'ma trompé et j'en suis désolé

      dummy () {
      char toto[10]; // sur la pile
      char * titi = "blabla"; // dans DATA
      }

      Mais l'idée qui était importante c'est que :
      a = malloc(...);
      a = "blabla";

      Le malloc est complètement inutile...

      Support linux
      (FReEDoM qui se tape un délire de bénévolat actuellement)

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.