1
votes

Tableau dynamique de structures en c (malloc, realloc)

J'essaie de créer un tableau d'éléments de structure et de les réaffecter si j'ai besoin de nouveaux éléments. Le tableau doit être un pointeur car je veux le renvoyer à partir de la fonction.

J'ai le code suivant:

  #include <stdio.h>
  #include <stdlib.h>

  struct rle_element {
    int length;
    char character;
  };

  void get_rle() {
    struct rle_element *rle = malloc(sizeof(struct rle_element));
    struct rle_element **rle_pointer = &rle;

    rle = realloc(rle, sizeof(rle) + sizeof(struct rle_element));

    rle[0].length = 10;
    printf("%d\n", rle[0].length);

    rle[1].length = 20;
    printf("%d\n", rle[1].length);
  }

  int main() {
      get_rle();

      return EXIT_SUCCESS;
  }

Mais ce code ne fonctionne pas vraiment. Je pense que la réaffectation n'est pas correcte.

J'ai maintenant le code suivant qui fonctionne très bien. Mais je peux aussi utiliser rle [2] ou rle [3] sans allocate. Je pense que mon programme n'alloue de l'espace que pour deux éléments.

   #include <stdio.h>
   #include <stdlib.h>

  struct rle_element {
    int length;
    char character;
  };

  void get_rle() {
    struct rle_element *rle = malloc(sizeof(struct rle_element*));
    struct rle_element **rle_pointer = &rle;

    rle = realloc(rle, sizeof(rle) + sizeof(struct rle_element*));

    (*rle_pointer)->length = 10;
    printf("%d\n", (*rle_pointer)->length);

    rle_pointer = &rle+1;

    (*rle_pointer)->length = 20;
    printf("%d\n", (*rle_pointer)->length);

  }

  int main() {
      get_rle();

      return EXIT_SUCCESS;
  }


5 commentaires

Vous avez oublié de poser une question.


Qu'entendez-vous par ne fonctionne pas ? Que doit-il faire et que se passe-t-il réellement?


Lorsque j'exécute le code ci-dessus, il imprime 10 dans la première ligne et "Segmentation fault". Dans la deuxième ligne


sizeof (struct rle_element *) est la taille d'un pointeur (probablement 4 ou 8 octets selon votre matériel). Vous voulez sizeof (struct rle_element) .


J'ai essayé cela mais j'obtiens le même message d'erreur.


3 Réponses :


0
votes

Bien qu'il soit déjà mentionné que vous allouez l'espace d'un pointeur et non la structure elle-même, il y a un autre problème.

rle += 1;

Ne vous donnera pas l'adresse d'un pointeur vers rle [1].

Avec l'autre changement proposé, vous obtiendrez toujours un segfault, mais si vous essayez également

 rle_pointer = &rle+1;

Au lieu de

(*rle_pointer)->length = 20;
printf("%d\n", (*rle_pointer)->length);

Le code fonctionnera avec succès. Cela indiquerait que votre realloc () a fonctionné avec succès.

Pour expliquer un peu mieux, un double pointeur est en fait un pointeur vers un pointeur. Il n'y a pas de pointeur sur & rle + 1. Votre code tente de déréférencer un pointeur qui n'existe pas.

Au lieu de

rle[1].length=20;
printf("%d\n", rle[1].length);

Vous pourriez simplement dire

rle_pointer = &rle+1;

Si vous insistez en procédant de cette façon. Sinon, je suggérerais de s'en tenir à l'utilisation de rle comme tableau et d'éviter le double pointeur.

Pour la mise à jour: c'est l'écriture et la lecture de la mémoire non allouée (causant UB) afaik.

Vous peut valider cela en libérant rle et en essayant de faire le même comportement, sur gcc 7.4.0 je peux faire de même.


2 commentaires

J'essaye d'utiliser rle comme tableau.


Vous ne devriez pas avoir besoin d'un rle_pointer alors. J'ai mis à jour un peu à la fin pour expliquer le problème suivant, afaik que la lecture et l'écriture dans la mémoire non allouée provoquent UB qui fonctionne.



1
votes

Il y a divers problèmes avec votre code (signalés dans les commentaires et autres réponses). Une chose essentielle qui vous manque est que vous ne suivez pas la taille de la région allouée. sizeof () est évalué au moment de la compilation (sauf si vous utilisez des VLA C99). Donc, faire sizeof (rle) ne va pas être évalué au nombre d'octets que votre tableau utilise. Vous devez stocker cela séparément. Voici une implémentation fonctionnelle qui suit la taille avec une structure struct rle_parent avec des commentaires inclus pour vous aider à comprendre:

#include <stdio.h>
#include <stdlib.h>

struct rle_element {
    int length;
    char character;
};

struct rle_parent {
    struct rle_element *arr;
    size_t count;
};

static void get_rle(struct rle_parent *p, int length, char character) {
    struct rle_element *e;

    /* Allocate enough space for the current number of elements plus 1 */
    e = realloc(p->arr, (p->count + 1) * sizeof(struct rle_element));
    if (!e) {
        /* TODO: (Re)allocation failed, we should handle this */
        return;
    }

    /* Update the parent to point to the reallocated array */
    p->arr = e;

    /* Update our newly added element */
    e = &p->arr[p->count];
    e->length = length;
    e->character = character;

    /* Bump the count so we know how many we have */
    p->count++;
}

int main() {
    struct rle_parent p;
    size_t i;

    /* Initialize */
    p.arr = NULL;
    p.count = 0;

    get_rle(&p, 10, 'a');
    get_rle(&p, 20, 'b');
    get_rle(&p, 30, 'c');

    /* Print out our array */
    for (i = 0; i < p.count; i++) {
        struct rle_element *e = &p.arr[i];
        printf("%d -- %c\n", e->length, e->character);
    }

    return 0;
}

Alternativement, si vous ne voulez pas maintenir un compte, vous pouvez ajouter un autre champ à struct rle_element qui indique qu'il s'agit du dernier élément du tableau. Vous devrez le mettre à jour à chaque fois que vous ajoutez un nouvel élément (en l'effaçant sur le dernier élément "courant" et en le définissant sur le dernier élément "nouveau"), mais vous pourriez vous débarrasser de la struct rle_parent code > dans ce cas.

De plus, en passant NULL comme premier argument à realloc () , il se comporte comme un appel à malloc () , donc ça marche proprement ici.


0 commentaires

0
votes

malloc et realloc nécessitaient la taille de l'espace pour réserver (allouer) dans la mémoire, donc si vous voulez faire un tableau vous devez allouer la taille de 1 élément multiplié par le nombre d'éléments dans le tableau, je vous suggère créer une variable pour contenir la taille du tableau. BTW "Erreur de segmentation" pour autant que je sache, signifie que vous utilisez un espace que vous n'avez pas alloué.


0 commentaires