Динамическое перераспределение многомерного массива - PullRequest
1 голос
/ 27 декабря 2010

У меня проблемы с функцией realloc.

Я выделяю динамический двумерный массив с помощью этой функции:

Bubble ***allocBubblesMatrix(int height, int width) {
  Bubble ***bubblesMatrix = (Bubble***) malloc(height * sizeof(Bubble**));
  assert(bubblesMatrix != NULL);
  int i;
  for (i = 0; i < height; ++i) {
    bubblesMatrix[i] = (Bubble**) malloc(width * sizeof(Bubble*));
    assert(bubblesMatrix[i] != NULL);
  }
  int x, y;  
  for (y = 0; y < height; ++y)
    for (x = 0; x < width;  ++x)
      bubblesMatrix[y][x] = newBubble(rand() % N_BUBBLES);

  return bubblesMatrix;
}

который вызывается со следующим кодом:

int matrixHeight = 1, 
    matrixWidth  = MATRIX_X_SIZE;
Bubble ***bubblesMatrix = allocBubblesMatrix(matrixHeight, matrixWidth);

Это успешно создает двумерный массив 1 * MATRIX_X_SIZE.

Затем я хочу добавить в матрицу строку или несколько строк, поэтому я использую realloc со следующей функцией. Предполагается добавить heightIncrement строк. Проблема в том, что иногда это работает, а другое - вылетает программа.

void resizeBubblesMatrix(Bubble ****bubblesMatrix, int height, int width, 
                         int heightIncrement) {
  if (heightIncrement <= 0) /* temporary */
    return;

  *bubblesMatrix = (Bubble***) realloc(*bubblesMatrix, (height + heightIncrement) * sizeof(Bubble**));
  assert(bubblesMatrix != NULL);
  int x, y;
  int newHeight = height + heightIncrement;

  for (y = height; y < newHeight; ++y) {
    (*bubblesMatrix)[y] = (Bubble**) malloc(width * sizeof(Bubble*));
    assert((*bubblesMatrix)[y] != NULL);
    for (x = 0; x < width; ++x)
      (*bubblesMatrix)[y][x] = newBubble(rand() % N_BUBBLES);
  }
}

Эта функция вызывается с помощью:

while(true) {
  drawBubblesMatrix(x1, y1, matrixHeight, matrixWidth, &bubblesMatrix, bubbles);
  resizeBubblesMatrix(&bubblesMatrix, matrixHeight, matrixWidth, 1);
  ++matrixHeight;
  getch();
  clear_screen(1);
}

Что я делаю не так?

Функция для освобождения ранее выделенных блоков памяти:

void freeBubblesMatrix(Bubble ****bubblesMatrix, int height, int width) {
  int y, x;
  for (y = 0; y < height; ++y) {
    for (x = 0; x < width; ++x) {
      free((*bubblesMatrix)[y][x]);
      (*bubblesMatrix)[y][x] = NULL;
    }
    free((*bubblesMatrix)[y]);
    (*bubblesMatrix)[y] = NULL;
  }
  free(*bubblesMatrix);
  *bubblesMatrix = NULL;
}

Заранее спасибо.

EDIT

  1. Глупый я. Я ничего не делал с возвращаемым значением realloc, как указал Карл Кнехтель. Но теперь программа вылетает при каждом запуске.
  2. Ответом Барта ван Ингена Шенау я подтвердил то, чего боялся: я игнорировал несколько независимых блоков памяти, которые я выделил ранее. Я даже получил код, похожий на тот, что написал Барт, но он продолжает сбой программы.
  3. Я добавил assert для проверки результатов вызовов malloc / realloc, и все же мне не повезло. Я использую djgpp с Win98, и то, что происходит, действительно странно:
    • Windows: иногда он никогда не падает; другие вылетает после добавления 2 строк.
    • MS-DOS: сбой после добавления 2 строк. Я попытаюсь использовать -O3 с gcc, чтобы получить дополнительные подсказки. Что может быть полезным (и быстрым в изучении / использовании) средством повреждения памяти / обнаружения утечек для Windows? Является ли Purify лучшим решением?
  4. Даже моя функция по освобождению массива возвращает ошибки страницы.

Ответы [ 4 ]

3 голосов
/ 27 декабря 2010

В использовании realloc есть ряд ошибок.

  1. Вы передаете неправильный указатель на realloc. Вы должны передать указатель, который вы получили из malloc, который будет *bubblesMatrix.
  2. «Расположение» матриц в функциях allocBubblesMatrix и resizeBubblesMatrix отличается. В функции alloc вы выделяете несколько независимых блоков памяти, но в функции resize вы рассматриваете это как один большой блок памяти. Это просто не сработает.

Правильное использование будет:

void resizeBubblesMatrix(Bubble ****bubblesMatrix, int height, int width, 
                         int heightIncrement) {
  *bubblesMatrix = (Bubble ***) realloc(*bubblesMatrix, (height + heightIncrement) * sizeof(Bubble**));
  int i;
  int newHeight = height + heightIncrement;
  for (i = height; i < newHeight; ++i)
    (*bubblesMatrix)[i] = (Bubble**) malloc(width * sizeof(Bubble*));
  int x, y;
  for (y = height; y < newHeight; ++y)
    for (x = 0; x < width; ++x)
      (*bubblesMatrix)[y][x] = newBubble(rand() % N_BUBBLES);
}

Но у этой функции все еще есть некоторые проблемы:

  • Оба malloc и realloc могут потерпеть неудачу, что здесь не учитывается
  • Если heightIncrement отрицательно, у вас утечка памяти в функции изменения размера.
3 голосов
/ 27 декабря 2010

Прочитайте документацию :

Функция может переместить блок памяти в новое место, и в этом случае новое местоположение возвращается .... Указатель наперераспределенный блок памяти, который может совпадать с аргументом ptr или новым местоположением.Тип этого указателя void *, который может быть приведен к желаемому типу указателя данных, чтобы быть разыменованным.Если функции не удалось выделить запрошенный блок памяти, возвращается указатель NULL, а блок памяти, на который указывает аргумент ptr, остается неизменным.

Нельзя правильно использовать realloc, не делая ничегос возвращаемым значением.Ваш код прямо сейчас ожидает, что realloc всегда сможет перераспределить память таким образом, чтобы новый блок находился в том же месте.Это явно невозможно;память сразу после вашего массива может быть использована для чего-то другого.

2 голосов
/ 30 декабря 2010

Я собрал быстрый тестовый пример и пришел к выводу, что проблема, с которой вы сейчас столкнулись, не в этом блоке кода.Я создал очень простой контрольный пример, который заменяет объекты Bubble на целые.Когда я делаю это, перераспределение успешно завершается без сбоев.Вот мой код:

#include <malloc.h>
#include <assert.h>

int myVal = 0xDEAD;

int ***allocBubblesMatrix(int height, int width);
void resizeBubblesMatrix(int ****bubblesMatrix, int height, int width, 
                         int heightIncrement);

int main(int argc, char **argv)
{
  int matrixHeight = 1, matrixWidth = 10;
  int i = 0;
  int ***matrix = allocBubblesMatrix(matrixHeight, matrixWidth);
  for(i = 1; i < matrixWidth; i++)
    resizeBubblesMatrix(&matrix, matrixHeight, matrixWidth, 1);
  printf("Complete!\n");
}

int ***allocBubblesMatrix(int height, int width) {
  int ***bubblesMatrix = (int***) malloc(height * sizeof(int**));
  assert(bubblesMatrix != NULL);
  int i;
  for (i = 0; i < height; ++i) {
    bubblesMatrix[i] = (int**) malloc(width * sizeof(int*));
    assert(bubblesMatrix[i] != NULL);
  }
  int x, y;  
  for (y = 0; y < height; ++y)
    for (x = 0; x < width;  ++x)
      bubblesMatrix[y][x] = &myVal;

  return bubblesMatrix;
}

void resizeBubblesMatrix(int ****bubblesMatrix, int height, int width, 
                         int heightIncrement) {
  if (heightIncrement <= 0) /* temporary */
    return;

  *bubblesMatrix = (int***) realloc(*bubblesMatrix, (height + heightIncrement) * sizeof(int**));
  assert(bubblesMatrix != NULL);
  int x, y;
  int newHeight = height + heightIncrement;

  for (y = height; y < newHeight; ++y) {
    (*bubblesMatrix)[y] = (int**) malloc(width * sizeof(int*));
    assert((*bubblesMatrix)[y] != NULL);
    for (x = 0; x < width; ++x)
      (*bubblesMatrix)[y][x] = &myVal;
  }
}

Единственные изменения, которые я сделал, - это заменить Bubble на int и указать все записи в матрице на одну переменную int вместо того, чтобы делать еще больше выделения.

Это означает, что ошибка либо в drawBubblesMatrix (), либо в newBubble ().

1 голос
/ 27 декабря 2010

Вам нужно перераспределить каждое измерение отдельно. Вы не можете перераспределить оба измерения одновременно, поскольку каждая «строка» была выделена отдельно.

*bubblesMatrix = (Bubble ***) realloc(bubblesMatrix, (height + heightIncrement) * width * sizeof(Bubble*));

необходимо изменить на

*bubblesMatrix = (Bubble ***) realloc(*bubblesMatrix, (height + heightIncrement) * sizeof(Bubble**));
...