C Allocation du pointeur et de la mémoire: tableaux Realloc et passage de pointeur

Pour ceux expérimentés avec C, ce sera un simple problème d’allocation de mémoire / référencement:

Voici mes structures de données:

struct configsection { char *name; unsigned int numopts; configoption *options; }; typedef struct configsection configsection; struct configfile { unsigned int numsections; configsection *sections; }; typedef struct configfile configfile; 

Voici mes routines pour initialiser une configuration ou un fichier de configuration, et pour append une configuration à un fichier de configuration:

 // Initialize a configfile structure (0 sections) void init_file(configfile *cf) { cf = malloc(sizeof(configfile)); cf->numsections = 0; } // Initialize a configsection structure with a name (and 0 options) void init_sec(configsection *sec, char *name) { sec = malloc(sizeof(configsection)); sec->numopts = 0; sec->name = name; printf("%s\n", sec->name); } // Add a section to a configfile void add_sec(configfile *cf, configsection *sec) { // Increase the size indicator by 1 cf->numsections = cf->numsections + 1; // Reallocate the array to accommodate one more item cf->sections = realloc(cf->sections, sizeof(configsection)*cf->numsections); // Insert the new item cf->sections[cf->numsections] = *sec; } 

Je crois que mon problème provient de ma fonction init_sec (). Voici un exemple:

 int main(void) { // Initialize test configfile configfile *cf; init_file(cf); // Initialize test configsections configsection *testcs1; init_sec(testcs1, "Test Section 1"); // Try printing the value that should have just been stored printf("test name = %s\n", testcs1->name); 

Bien que la fonction printf() de init_sec() imprime correctement le nom que je viens de stocker dans la configuration, une tentative identique dans la fonction printf() de main() génère une erreur de segmentation. De plus, addsec() génère une erreur de segmentation.

    Cette routine devrait être

     void init_file(configfile **cf) { *cf = malloc(sizeof(configfile)); (*cf)->numsections = 0; (*cf)->sections = NULL; // You forgot to initialise this. } 

    c’est à dire appelé par init_file(&myconfigfilepointer); de sorte que la valeur de retour malloc est renvoyée.

    Besoin de faire la même astuce pour init_sec

    Cette fonction est incorrecte – voici une version corrigée

     void add_sec(configfile *cf, configsection *sec) { // Increase the size indicator by 1 // Reallocate the array to accommodate one more item cf->sections = realloc(cf->sections, sizeof(configsection)*(1 + cf->numsections)); // Insert the new item cf->sections[cf->numsections] = *sec; // Since arrays start at 0 cf->numsections = cf->numsections + 1; } 

    Vous devez ensuite ajuster les appels en mode main

    À aucun moment, vous cf->sections , ce qui signifie que lorsque vous essayez de le realloc pour la première fois, vous passez des déchets. Ajouter:

      cf->sections = NULL; 

    à init_file devrait aider.

    Vous ne vérifiez pas non plus les codes de retour, mais vous saviez que oui?

    Vous devez passer un pointeur de la valeur à mettre à jour … par exemple:

     // Initialize a configfile structure (0 sections) void init_file(configfile **cf) { *cf = malloc(sizeof(configfile)); (*cf)->numsections = 0; } configfile *var; init_file(&var); printf("%d\n", var->numsections); 

    Sinon, vous ne faites que mettre à jour le pointeur local * cf et non l’original passé en valeur

    Vous devez vraiment repenser la façon dont les arguments de la fonction sont passés en C et quels pointeurs sont. Votre problème n’a rien à voir avec l’allocation de mémoire. Au lieu de cela, votre code affecte un pointeur à la mémoire allouée dynamicment uniquement à une variable locale , dont le code appelant ne sait rien.

    Bien que vous puissiez résoudre le problème en passant un pointeur au pointeur de l’appelant (c’est-à-dire un double pointeur), ce n’est pas nécessairement la manière la plus élégante ou la plus habituelle de gérer les choses. Vous devriez plutôt renvoyer le résultat de l’allocation à partir de la fonction. Pendant que vous y êtes, vous devriez également utiliser calloc pour mettre calloc à zéro la mémoire. Envelopper le tout:

     typedef struct substuff_ { int a; double b; } substuff; typedef struct stuff_ { unsigned int n; substuff * data; } stuff; substuff * init_substuff() { substuff * const p = malloc(sizeof *p); if (p) { p->a = 5; p->b = -0.5; } return p; } stuff * init_stuff() { substuff * const p = init_substuff(); if (!p) return NULL; stuff * const q = malloc(sizeof *q); if (q) { q->n = 10; q->data = p; } return q; } 

    À titre d’exercice, vous devez écrire les fonctions correspondantes void free_substuff(substuff *) et void free_stuff(stuff *) .

    Oui, il y a un problème dans init_sec

     // Initialize a configsection structure with a name (and 0 options) void init_sec(configsection *sec, char *name) { sec = malloc(sizeof(configsection)); sec->numopts = 0; sec->name = name; printf("%s\n", sec->name); } 

    Vous ne faites que copier le pointeur de nom ici, ce qui signifie qu’il pointe vers le stockage d’origine du nom. Si vous appelez init_sec comme ceci

     configsection foobar() { configsection sec; char name[80]; get_name(name); init_sec(sec, name); return sec; } 

    Le pointeur du name est devenu invalide au moment où foobar est revenu Vous devez dupliquer la chaîne et conserver votre copie privée. En init_sec :

      sec->name = strdup(name); 

    Mais il y a plus. Dans la toute première ligne d’ init_sec vous init_sec le pointeur qui a été transmis à init_sec par celui de malloc. Ainsi, le nouveau pointeur n’est jamais renvoyé à la requête. Soit utilisez un pointeur vers un pointeur, ne prenez pas du tout un pointeur de configuration (après tout, vous allouez), mais renvoyez simplement le pointeur alloué: Fonction corrigée complète:

     // Initialize a configsection structure with a name (and 0 options) configsection* init_sec(char *name) { configsection *sec = malloc(sizeof(configsection)); sec->numopts = 0; sec->name = name; printf("%s\n", sec->name); return sec; }