C- возможная утечка памяти?

#c #data-structures #multidimensional-array #struct #free

#c #структуры данных #многомерный массив #структура #Бесплатно

Вопрос:

Я чувствую, что фрагмент кода, который у меня есть, может вызвать утечку памяти. У меня есть структура данных с двумя двумерными массивами, один из которых содержит целые числа, а другой содержит указатели на динамически выделяемые объекты (спрайты). Структура данных представляет собой tilemap, а целые числа представляют собой числовой индекс каждого местоположения, который считывается из файла. Я называю этот индекс ’tiles’. Это указывает, что это за плитка, для поведенческих целей (т. Е. Игрок реагирует на воду иначе, чем на грязь или лед). Объекты представляют собой спрайты для рисования в соответствующих местах. Этот индекс известен как «изображения». Этот индекс сообщает tilemap, какой спрайт рисовать в этой позиции.

 typedef struct
{
    int** tiles;
    sprite*** images;
    int w, h;
} tilemap;
  

У меня есть функция, которая создает новую tilemap, инициализирует ее и возвращает.

 tilemap* new_tilemap(int w, int h, const char* filename)
{
    tilemap* tm = malloc(sizeof(tilemap));
    tm->w = w;
    tm->h = h;

    /*allocate memory space for the tiles index*/
    tm->tiles = malloc(sizeof(int) * h);
    int i, j;
    for (i = 0; i < h;   i)
    {
        tm->tiles[i] = malloc(sizeof(int) * w);
    }

    /*fill the index with the appropriate data from a file*/
    FILE* file = fopen (filename, "rb");
    if (file == NULL)
    {
        printf("Failed to open map %sn", filename);
    }

    for (j = 0; j < h;   j)
    {
        for (i = 0; i < w;   i)
        {
            fscanf(file, "%d", amp;(tm->tiles[j][i]));
        }
    }
    fclose(file);

    /*allocate space for the images*/
    tm->images = malloc(sizeof(sprite*) * h);
    for (i = 0; i < h;   i)
    {
        tm->images[i] = malloc(sizeof(sprite*) * w);
    }

    /*load images based on what type of tile is at that position*/
    for (j = 0; j < h;   j)
    {
        for (i = 0; i < w;   i)
        {
            switch (tm->tiles[j][i])
            {
                case 0:
                tm->images[j][i] = new_sprite_file("dat/tiles/0.bmp", 1);
                break;
                case 1:
                tm->images[j][i] = new_sprite_file("dat/tiles/1.bmp", 2);
                break;
            }
            tm->images[j][i]->x = i*tm->images[j][i]->w;
            tm->images[j][i]->y = j*tm->images[j][i]->h;
        }
    }
    return tm;
}
  

Затем, чтобы освободить tilemap и все его структуры, у меня есть эта функция:

 void free_tilemap(tilemap* tm)
{
    /*loop through and free each of the images in the array*/
    int i, j;
    for (j = 0; j < tm->h;   j)
    {
        for (i = 0; i < tm->w;   i)
        {
            free(tm->images[j][i]);
         }
    }
    /*free the actual array*/
    free(tm->images);
    /*free the tile array?*/
    free(tm->tiles);
    /*free the entire tilemap structure*/
    free(tm);
}
  

Однако я чувствую, что это не освобождает всю выделенную мной память, потому что я дважды использовал malloc на плитках, но освободил только один раз. Я не знаю, является ли это проблемой, поскольку они являются целыми числами, но я думаю, что мне, возможно, придется перебирать массив tiles, освобождать каждую строку, затем перебирать и освобождать каждый столбец (содержащий строки) таким же образом, как он был выделен. Это то, что нужно сделать, или я просто невежествен и / или параноик? То же самое с массивом изображений. Кроме того, не стесняйтесь указывать на другие недостатки в моем коде, поскольку я знаю, что я не лучший программист.

Комментарии:

1. Некоторые из ваших типов malloc неверны. tiles должно быть malloc(sizeof(int*) * N) , и images должно быть malloc(sizeof(sprite**) * M) , и т.д.

2. Рассматривали ли вы возможность использования инструмента профилирования памяти, такого как valgrind в Linux или purify в Windows?

3. @Keelx: вы говорите, чтобы выделить память для N элементов типа T T * p = malloc(sizeof(T) * N); . Теперь примените эту логику к случаям `T = int *` и T = sprite** в соответствующих ситуациях. И не забывайте free() обо всем, что вы выделяете. Осмелюсь предположить, что если вы не совсем согласны с этими идеями, ощущение «большей продуктивности с C», вероятно, вводит в заблуждение…

4. @KerrekSB Хорошо, я понимаю, что ты говоришь. Внося изменения, valgrind не показывает изменений в объеме используемой памяти при выходе по сравнению с программой без предложенных вами изменений.

5. @Keelx: почти гарантировано, что вы забыли что-то очистить. Вы можете использовать valgrind, чтобы показать, где была выделена доступная память, чтобы получить представление.

Ответ №1:

Конечно, вы должны отразить malloc s при освобождении.

 for (i = 0; i < h;   i)
{
    tm->tiles[i] = malloc(sizeof(int) * w);
}

/* Inside free_tilemap. */
for (i = 0; i < h;   i)
{
    free(tm->tiles[i]);
}
free(tm->tiles);
  

То же самое относится и к другим for , которые очень похожи на этот. Освобождение просто tiles не происходит автоматически tiles[0..h] в каскаде.

Комментарии:

1. Спасибо. Тоже хорошее объяснение. А как насчет массива изображений?

2. @Keelx tm->images[i] = malloc(sizeof(sprite*) * w) заставляет меня задуматься free(tm->images[i]) .

3. Это то, что я подумал. Спасибо!

Ответ №2:

Быстро просмотрев код, я бы сказал, что вам действительно не хватает свободных мест на плитках. Я бы посоветовал использовать анализатор памяти, чтобы выяснить это самостоятельно. Например. http://www.cprogramming.com/debugging/valgrind.html Это даст вам хороший обзор выделенной памяти и возможных утечек памяти при выходе из программы.

Комментарии:

1. Большое вам спасибо, похоже, это очень полезный инструмент для подобных вещей.