Динамический массив структур в c (malloc, realloc) - PullRequest
0 голосов
/ 23 января 2019

Я пытаюсь создать массив элементов структуры и перераспределить, если мне нужны новые элементы.Массив должен быть указателем, потому что я хочу вернуть его из функции.

У меня есть следующий код:

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

Но этот код на самом деле не работает.Я думаю, что перераспределение не правильно.

Теперь у меня есть следующий код, который работает нормально.Но я также могу использовать rle [2] или rle [3] без allocate.Я думаю, что моя программа выделяет место только для двух элементов.

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

Ответы [ 3 ]

0 голосов
/ 23 января 2019

Существуют различные проблемы с вашим кодом (указано в комментариях и других ответах).Одна важная вещь, которую вы упускаете, это то, что вы не отслеживаете размер выделенной области.sizeof() оценивается во время компиляции (если вы не используете C99 VLA).Таким образом, выполнение sizeof(rle) не приведет к количеству байтов, которые использует ваш массив.Вы должны хранить это отдельно.Вот рабочая реализация, которая отслеживает размер с помощью структуры struct rle_parent с комментариями, которые помогут вам понять:

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

В качестве альтернативы, если вы не хотите вести подсчет, вы можете добавить другое поледо struct rle_element, что указывает на то, что это последний элемент в массиве.Вам придется обновлять его каждый раз, когда вы добавляете новый элемент (очищая его в «текущем» последнем элементе и устанавливая его в «новом» последнем элементе), но в этом случае вы можете избавиться от struct rle_parent.

Кроме того, передача NULL в качестве первого аргумента realloc(), заставляет его вести себя как вызов malloc(), поэтому здесь все работает чисто.

0 голосов
/ 24 января 2019

malloc и realloc требовали размер пространства, которое нужно зарезервировать (выделить) в памяти, поэтому, если вы хотите создать массив, вы должны выделить размер 1 элемента, умноженный на количество элементов в массиве, я предлагаю вам сделать переменную для хранения размера массива. Кстати, «ошибка сегментации», насколько я знаю, означает, что вы используете пространство, которое вы не выделяли.

0 голосов
/ 23 января 2019

Хотя уже упоминалось, что вы выделяете пространство указателя, а не саму структуру, есть еще одна проблема.

rle_pointer = &rle+1;

Будет не получить адрес указателя на rle [1].

С другим предложенным изменением вы все равно получите ошибку, но если вы попробуете

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

Вместо

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

У вас будет успешно работать код. Это будет означать, что ваш realloc () работал успешно

Чтобы объяснить немного лучше, двойной указатель на самом деле является указателем на указатель. Нет указателя на & rle + 1. Ваш код пытается разыменовать указатель, который не существует.

Вместо

 rle_pointer = &rle+1;

Вы могли бы просто сказать

rle += 1;

Если вы настаиваете на этом. В противном случае я бы предложил использовать rle в качестве массива и избегать двойного указателя.

Для обновления: это запись и чтение нераспределенной памяти (вызывающей UB) afaik.

Вы можете проверить это, освободив rle и попытавшись сделать то же самое поведение, на gcc 7.4.0 я могу сделать то же самое.

...