动态大小数据库内存泄漏求助:已释放内存仍存在泄漏
动态数据库内存泄漏问题排查
我尝试创建一个支持用户输入指定大小的动态数据库,但多次调整数据库大小后出现了内存泄漏。我觉得已经正确释放了所有内存,却找不到问题出在哪。
第三次修改说明
我移除了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; }
问题分析
循环边界越界
C语言数组索引从0开始,大小为N的数组有效索引范围是0到N-1。你的代码中使用i <= newSize或i <= *dbSize的循环条件,会访问超出数组边界的内存,引发未定义行为,可能导致内存泄漏或程序崩溃。
更严重的是,首次调用newDatabase时,database为NULL、dbSize为0,进入else分支后循环i <= 0会执行database[0].product,直接触发空指针解引用崩溃。未处理初始空数据库场景
当database为NULL(首次创建数据库)时,不需要执行任何释放操作,直接分配新内存即可,原代码的释放逻辑会触发非法访问。程序退出时未清理内存
在case 'q'分支中,没有释放最后一次分配的database及其内部可能存在的product指针,导致内存泄漏。逻辑错误:调整大小时错误释放所有数据
代码在调整数据库大小会释放原数据库的所有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
相关产品推荐
相关产品推荐

