【问题标题】:Free Memory of 2d Array in struct结构中二维数组的可用内存
【发布时间】:2021-11-19 14:10:48
【问题描述】:

我正在用 C 语言编写一个小游戏,我想用 Valgrind 对其进行测试。 这是一个小代码示例:

#include <stdio.h>
#include <stdlib.h>

typedef struct Game {
    int** field;
} Game;

void buildfield(Game* game, int length);
void printfield(Game* game, int length);
void freefield(Game* game, int length);

int main()
{
    struct Game* game = NULL;
    game = malloc(sizeof (struct Game));
    buildfield(game, 10);
    printfield(game, 10);
    freefield(game, 10);
    free(game);
    return 0;
}

void buildfield(Game* game, int length)
{
    game->field = (int**)malloc((sizeof (int*)) * 20);
    int i;
    for (i = 0; i < 20; i++) {
        game->field[i] = (int*) malloc((sizeof (int)) * length);
    }

    for (int line = 0; line < length; line++) {
        for (int col = 0; col < 81; col++) {
            game->field[col][line] = 0;
        }
    }
}

void printfield(Game* game, int length)
{
    for (int i = 0; i < length; i++) {
        printf("\n");
        for (int j = 0; j < 20; j++) {
            printf("%d",game->field[i][j]);
        }
    }
}

void freefield(Game* game, int length)
{
    for (int i = 0; i < length; ++i) {
        free(game->field[i]);
    }
    free(game->field);
}

Valgrind 说:

HEAP SUMMARY:
==6239==     in use at exit: 968 bytes in 22 blocks
==6239==   total heap usage: 22 allocs, 0 frees, 968 bytes allocated

我为什么不释放分配?

【问题讨论】:

  • for(i = 0; i &lt; 20; i++) 你分配了 20 个字段,但 freefield(game, 10); 只释放了 10 个字段
  • for(i = 0; i &lt; 20; i++)freefield(game, 10):使用 contants 或 #defines 代替硬编码数字,例如:#define NBFIELDS 20for(i = 0; i &lt; NBFIELDS; i++)freefield(game, NBFIELDS)
  • for (int col = 0; col &lt; 81; col++) - 你没有80列就够了,所以有一个越界访问,即未定义行为
  • 当我运行你的代码时,我得到了一个分段错误(@Ruks 上面提到的部分)所以当然free() 永远不会被调用,因为程序在那之前崩溃了。
  • 如果您停止使用“幻数”并改用命名变量/常量,这段代码中的所有问题都会消失。

标签: c malloc valgrind free


【解决方案1】:

“我为什么不释放分配?”

如 cmets 中所述,由于使用无法解释的值 (magic numbers) 来创建和释放内存,代码中存在额外的复杂性。由于链接中解释的原因以及其他问题,这可能会使空闲计数与分配计数的匹配变得困难。由于 Valgrind 指示在执行结束时剩余的内存块,因此调用这些中的每一个的时间不匹配。

以下是带有建议的代码,包括明确将相同数量的调用应用于free()[m][c]alloc() 的代码(我选择在这里使用calloc() 以避免另一个循环(或memset())初始化内存。)

另请注意,您可能需要更改此示例用于#defines 的值以满足您的需要,但您只需在一处(文件顶部)更改它们。

typedef struct Game {
    int** field;
} Game;

void buildfield(Game *game, int length);
void printfield(Game *game, int length);
void freefield(Game *game, int length);

#define COUNT  20//replace all magic numbers 
#define LENGTH 10//(change values of #defines to change shape and size of memory)

int main(void)//use a complete prototype for main
{
    struct Game* game = NULL;//pointer needs memory
    game = malloc(sizeof (struct Game));

    buildfield(game, LENGTH);
    printfield(game, LENGTH);
    freefield(game, LENGTH);
    free(game);
    return 0;
}

void buildfield(Game *game, int length)
{   //Note - not optimal to cast return of [c][m]alloc in C (only in C++)
    game->field = calloc(COUNT, sizeof(game->field));
    int i;
    for (i = 0; i < COUNT; i++) {
        game->field[i] = calloc(LENGTH, (sizeof (game->field[i])) );
    }
}

void printfield(Game *game,int length)
{
    for (int i = 0; i < COUNT; i++) {
        printf("\n");
        for (int j = 0; j < LENGTH; j++) {
            printf("%d",game->field[i][j]);
        }
    }
}

void freefield(Game *game,int length)
{
    for (int i = 0; i < COUNT; ++i) {
        free(game->field[i]);
    }
    free(game->field);
}

【讨论】:

    【解决方案2】:

    在函数 buildfield() 你已经分配了 20 块内存

    game->field = (int **) malloc(sizeof(int *) * 20);

    因此,您可以通过 [] 运算符访问最多的是 game->field[19] 但在循环中,程序会尝试在 game->field[19] 之前访问更多块 导致分段错误 然后程序崩溃了。无需返回 main(),更不用说到达 free() 语句了。所以这意味着你的程序从一开始就没有完成并且在中途崩溃了。

    for (int line = 0; line < length; line++)
    {
        for (int col = 0; col < 81; col++)  //when col becomes 20
        {
            game->field[col][line] = 0;     //this statement will try to access the non-alloted memory block when col is 20
        }
    }
    

    要检查您的程序是否在中途崩溃,请在与访问内存相关的语句末尾添加一些打印语句或使用调试器。因为这是最常见的运行时错误来源。

    对于此代码,请记住最大编号。分配的内存块可以由特定指针访问,并更改标记的 for 循环的条件,以便它将在分配限制内访问内存,即 no。您使用 malloc 函数分配的块数。

    这里的问题不是 free() 不工作。但是分段错误。 假设您提供的代码是您的代码的精确副本

    【讨论】:

    • 要检查您的程序是否在中途崩溃添加一些打印语句或使用调试器
    猜你喜欢
    • 2018-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-21
    • 2021-10-02
    • 1970-01-01
    • 2021-09-22
    • 1970-01-01
    相关资源
    最近更新 更多