You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

动态大小数据库内存泄漏求助:已释放内存仍存在泄漏

动态数据库内存泄漏问题排查

我尝试创建一个支持用户输入指定大小的动态数据库,但多次调整数据库大小后出现了内存泄漏。我觉得已经正确释放了所有内存,却找不到问题出在哪。

第三次修改说明

我移除了product指针的malloc操作,改为通过if-else语句释放所有非NULL值。

原代码

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

typedef struct _product_t
{
    char *product;
    float price;
} product_t;

product_t *newDatabase(product_t *database, int *dbSize, int newSize)
{
    product_t *newdatabase = (product_t *)malloc(sizeof(*newdatabase) * newSize);

    if (newSize < *dbSize)
    {
        for (int i = 0; i <= newSize; i++)
        {
            if (database[i].product != NULL)
            {
                free(database[i].product);
            }
        }
    }
    else
    {
        for (int i = 0; i <= *dbSize; i++)
        {
            if (database[i].product != NULL)
            {
                free(database[i].product);
            }
        }
    }
    for (int i = *dbSize; i < newSize; i++)
    {
        // newdatabase[i].product = (char*)malloc(sizeof(char)*100);
        newdatabase[i].product = NULL;
        newdatabase[i].price = -1;
    }

    *dbSize = newSize;
    free(database);
    return newdatabase;
}
int main(void)
{
    product_t *database = NULL;
    int dbSize = 0;
    char cmd;
    do
    {
        printf("Command?");
        scanf(" %c", &cmd);
        switch (cmd)
        {

        case 'q':
            printf("Bye!");

            break;
        case 'n':
            printf("Size? ");
            int newSize2 = 0;
            scanf("%d", &newSize2);
            if (newSize2 < 0)
            {
                printf("Must be larger than 0");
                break;
            }
            else
            {
                database = newDatabase(database, &dbSize, newSize2);
                break;
            }
        default:
            printf("Unkown command '%c'\n", cmd);
        }
    } while (cmd != 'q');

    return 0;
}

问题分析

  1. 循环边界越界
    C语言数组索引从0开始,大小为N的数组有效索引范围是0到N-1。你的代码中使用i <= newSize或i <= *dbSize的循环条件,会访问超出数组边界的内存,引发未定义行为,可能导致内存泄漏或程序崩溃。
    更严重的是,首次调用newDatabase时,database为NULL、dbSize为0,进入else分支后循环i <= 0会执行database[0].product,直接触发空指针解引用崩溃。

  2. 未处理初始空数据库场景
    当database为NULL(首次创建数据库)时,不需要执行任何释放操作,直接分配新内存即可,原代码的释放逻辑会触发非法访问。

  3. 程序退出时未清理内存
    在case 'q'分支中,没有释放最后一次分配的database及其内部可能存在的product指针,导致内存泄漏。

  4. 逻辑错误:调整大小时错误释放所有数据
    代码在调整数据库大小会释放原数据库的所有product指针,相当于每次调整大小都会清空所有原有数据。如果需要保留数据,应该将原数据复制到新数据库中,而非直接释放。


修复后的代码

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

typedef struct _product_t
{
    char *product;
    float price;
} product_t;

product_t *newDatabase(product_t *database, int *dbSize, int newSize)
{
    product_t *newdatabase = (product_t *)malloc(sizeof(*newdatabase) * newSize);
    if (newdatabase == NULL) {
        perror("malloc failed");
        exit(EXIT_FAILURE);
    }

    // 初始化新数据库所有元素
    for (int i = 0; i < newSize; i++)
    {
        newdatabase[i].product = NULL;
        newdatabase[i].price = -1;
    }

    // 处理原数据库(如果存在)
    if (database != NULL) {
        // 确定需要保留的数据量
        int keepCount = (*dbSize < newSize) ? *dbSize : newSize;
        // 复制原有数据(如需保留)
        for (int i = 0; i < keepCount; i++) {
            if (database[i].product != NULL) {
                newdatabase[i].product = strdup(database[i].product);
                if (newdatabase[i].product == NULL) {
                    perror("strdup failed");
                    // 回滚已分配内存
                    for (int j = 0; j < i; j++) {
                        free(newdatabase[j].product);
                    }
                    free(newdatabase);
                    exit(EXIT_FAILURE);
                }
            }
            newdatabase[i].price = database[i].price;
        }

        // 释放原数据库的所有资源
        for (int i = 0; i < *dbSize; i++)
        {
            if (database[i].product != NULL)
            {
                free(database[i].product);
            }
        }
        free(database);
    }

    *dbSize = newSize;
    return newdatabase;
}

int main(void)
{
    product_t *database = NULL;
    int dbSize = 0;
    char cmd;
    do
    {
        printf("Command? ");
        scanf(" %c", &cmd);
        switch (cmd)
        {
        case 'q':
            printf("Bye!\n");
            // 退出前清理所有内存
            if (database != NULL) {
                for (int i = 0; i < dbSize; i++) {
                    if (database[i].product != NULL) {
                        free(database[i].product);
                    }
                }
                free(database);
            }
            break;
        case 'n':
            printf("Size? ");
            int newSize2 = 0;
            scanf("%d", &newSize2);
            if (newSize2 < 0)
            {
                printf("Must be larger than 0\n");
                break;
            }
            else
            {
                database = newDatabase(database, &dbSize, newSize2);
                break;
            }
        default:
            printf("Unknown command '%c'\n", cmd);
        }
    } while (cmd != 'q');

    return 0;
}

关键修复点

  • 修正循环边界为i < 大小,避免越界访问
  • 增加database非空判断,处理首次创建数据库的场景
  • 在程序退出时释放所有已分配的内存
  • 添加内存分配失败的检查与回滚逻辑
  • 保留原有数据的复制逻辑(如需清空数据可移除该部分)

内容的提问来源于stack exchange,提问作者Michael

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 18:11:10