简体   繁体   中英

Dynamic array of structs in c (malloc, realloc)

I am trying to create a array of structure elements and reallocate if I need new elements. The array has to be a pointer because I want to return it from the function.

I have the following code:

   #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;
  }

But this code isn't really working. I think the reallocate is not right.

Now I have the following code which works fine. But I can also use rle[2] or rle[3] without allocate. I think my programm only allocate space for two items.

  #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;
  }

There are various problems with your code (pointed out in comments and other answers). One critical thing that you are missing is that you are not tracking the size of the allocated region. sizeof() is evaluated at compile-time (unless you are using C99 VLAs). So doing sizeof(rle) is not going to evaluate to the number of bytes that your array is using. You have to store that separately. Here is a working implementation that tracks the size with a struct rle_parent structure with comments included to help you understand:

#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;
}

Alternatively, if you don't want to maintain a count, you could add another field to struct rle_element that indicates that it is the last element in the array. You would have to update that each time you add a new element (clearing it on the "current" last element and setting it on the "new" last element), but you could get rid of the struct rle_parent in that case.

Also, passing NULL as the first argument to realloc() , makes it behave like a call to malloc() , so it works out cleanly here.

While it's already mentioned that you're allocating the space of a pointer and not the structure itself, there's another, issue.

rle_pointer = &rle+1;

Will not get you the address of a pointer to rle[1].

With the other change proposed, you'll still get a segfault, however if you also try

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

Instead of

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

You'll have the code successfully work. This would indicate your realloc() has worked successfully.

To explain a bit better, a double pointer is actually a pointer to a pointer. There is no pointer at &rle+1. Your code is trying to dereference a pointer that doesn't exist.

Instead of

 rle_pointer = &rle+1;

You could just say

rle += 1;

If you insist upon doing it this way. Otherwise, I'd suggest sticking to using rle as an array and avoiding the double pointer.

For the update: it's writing and reading unallocated memory (causing UB) afaik.

You can validate this by freeing rle and trying to do the same behaviour, on gcc 7.4.0 I can do the same.

malloc and realloc required the size of the space to reserve(allocate) in the memory, so if you want to make an array you must allocate the size of 1 element multiplied by the number of elements in the array, I suggest you make a variable to hold the size of the array. BTW "Segmentation fault" as far as I know, means you are using space that you did not allocate for.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM