C语言可能存在内存泄漏问题?

3

我觉得我写的一段代码可能会导致内存泄漏。我的数据结构包含两个二维数组,一个包含int类型的数值,另一个包含指向动态分配对象(精灵)的指针。这个数据结构是一个瓷砖地图,其中int类型的数值是每个位置的数字索引,从文件中读取。我称之为“tiles”。这告诉我们它是什么类型的瓷砖,用于行为目的(例如玩家对水和土或冰的反应不同)。而对象则是要在各自位置绘制的精灵。这个索引被称为“images”,它告诉瓷砖地图在该位置绘制哪个精灵。

typedef struct
{
    int** tiles;
    sprite*** images;
    int w, h;
} 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 %s\n", filename);
    }

    for (j = 0; j < h; ++j)
    {
        for (i = 0; i < w; ++i)
        {
            fscanf(file, "%d", &(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;
}

然后,为了释放瓦片地图及其所有结构,我有以下函数:

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

然而,我感觉它并没有释放我分配的所有内存,因为我在tiles上使用了两次malloc,但只有一次free。我不知道这是否是一个问题,因为它们是整数,但我认为我可能需要循环遍历tiles数组,释放每一行,然后循环遍历并释放每一列(包含行)以相同的方式进行分配。这是需要做的吗,还是我只是无知和/或多虑?images数组也是如此。另外,如果我的代码存在其他缺陷,请随意指出,因为我知道我不是最好的程序员。

2
你的一些malloc类型是错误的。tiles 应该是 malloc(sizeof(int*) * N),而 images 应该是 malloc(sizeof(sprite**) * M),等等。 - Kerrek SB
2
您是否考虑过在Linux上使用Valgrind或在Windows上使用Purify等内存分析工具? - Alok Save
@Keelx:为了为类型为TN个元素分配内存,您可以使用T * p = malloc(sizeof(T) * N);。现在将此逻辑应用于情况T = int*T = sprite **中的各自情况。并且不要忘记释放您分配的所有内容。我敢说,如果您对这些想法不是完全熟悉,那么“使用C更具生产力”的感觉可能是一种误导性的感觉... - Kerrek SB
@KerrekSB 好的,我明白你的意思了。做出了更改后,valgrind 在退出时显示使用的内存量与没有采用你建议的更改的程序相比没有变化。 - Keelx
@Keelx:几乎可以确定你忘记清理某些东西了。你可以使用valgrind来显示可达内存的分配位置,以获得一些想法。 - Kerrek SB
@KerrekSB,我现在已经找到它们了。感谢您的帮助! - Keelx
2个回答

3
当然,在释放内存时应该对`malloc`进行镜像操作。
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]

谢谢。解释得很好。不过图片数组怎么办? - Keelx
@Keelx tm->images[i] = malloc(sizeof(sprite*) * w) 让我想到了 free(tm->images[i]) - cnicutar
那正是我想的。谢谢! - Keelx

2

快速查看代码,我会说你确实缺少对瓷砖的free。建议使用内存分析器自行查找。例如:http://www.cprogramming.com/debugging/valgrind.html。这将为您提供已分配内存的概述以及程序退出时可能存在的内存泄漏。


非常感谢,这似乎是一个非常有用的工具,特别适用于这种情况。 - Keelx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接