C语言致命堆损坏错误求助:退出菜单触发deleteList相关问题
堆损坏错误排查与修复
问题描述
选择菜单选项4(退出)时触发堆损坏错误:
HEAP CORRUPTION DETECTED: after Normal block (#76) at 0x00845EE8.
问题与deleteList函数相关,以下是完整代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define PRO_OP 1 #define CON_OP 2 #define PRINT_OP 3 #define EXIT_OP 4 #define STR_LEN 50 #define MAX_LIST_LENGTH 10 typedef struct reasonList { char* listName; char* reasons[MAX_LIST_LENGTH]; int numReasons; } reasonList; void initList(reasonList* list, char* name); void addReason(reasonList* list); void printList(reasonList list); int menu(void); void myFgets(char str[], int n); void deleteList(reasonList* list); int main(void) { char dilemma[STR_LEN] = { 0 }; int op = 0; reasonList proList; initList(&proList, "PRO"); reasonList conList; initList(&conList, "CON"); printf("What is your dilemma?\n"); myFgets(dilemma, STR_LEN); while (op != EXIT_OP) { op = menu(); switch (op) { case(PRO_OP): addReason(&proList); break; case(CON_OP): addReason(&conList); break; case(PRINT_OP): printf("Your dilemma:\n"); printf("%s\n\n", dilemma); printList(proList); printList(conList); break; case(EXIT_OP): deleteList(&proList); deleteList(&conList); break; } } printf("Good luck!\n"); getchar(); return 0; } /* Function will initialize a reason list input: the list to init, and its name output: none */ void initList(reasonList* list, char* listName) { list->listName = (char*) malloc(sizeof(char)); strcpy(list->listName, listName);//equal to PRO or CON for (int i = 0; i < MAX_LIST_LENGTH; i++) { list->reasons[i] = (char*)malloc(sizeof(char) * STR_LEN); if (list->reasons[i] == NULL) { printf("no memmory left\n"); exit(1); } strcpy(list->reasons[i], ""); } list->numReasons = 0; } /* Function will add a reason to the list input: the list to add to and its name output: none */ // void addReason(reasonList* list) { char* newReason = (char*)malloc(sizeof(char) * STR_LEN); printf("Enter a reason to add to the list %s:\n", list->listName); myFgets(newReason, STR_LEN); if (list->numReasons >= MAX_LIST_LENGTH)//if no memory left { //free(newReason); return; } free(list->reasons[list->numReasons]); list->reasons[list->numReasons] = newReason; list->numReasons++; list->reasons[list->numReasons] = NULL; } /* Function will print a list of reasons input: the list output: none */ void printList(reasonList list) { printf("list %s\n--------\n", list.listName); for (int i = 0; i < list.numReasons; i++) { printf("%s", list.reasons[i]); printf("\n"); } printf("\n"); } /* Function shows menu and returns user's choice input: none output: user's choice */ int menu(void) { int op = 0; printf("Choose option:\n"); printf("%d - Add PRO reason\n", PRO_OP); printf("%d - Add CON reason\n", CON_OP); printf("%d - Print reasons\n", PRINT_OP); printf("%d - Exit\n", EXIT_OP); scanf("%d", &op); while (op < PRO_OP || op > EXIT_OP) { printf("Invalid option. Try again: "); scanf("%d", &op); } getchar(); // clean buffer return op; } /* Function will delete a list input: the list to delete output: none */ void deleteList(reasonList* list) { for (int i = 0; i < list->numReasons; i++) { free(list->reasons[i]); list->reasons[i] = NULL; // set pointer to NULL } free(list->listName); list->listName = NULL; // set pointer to NULL } /* Function will perform the fgets command and also remove the newline that might be at the end of the string - a known issue with fgets. input: the buffer to read into, the number of chars to read */ void myFgets(char str[], int n) { fgets(str, n, stdin); str[strcspn(str, "\n")] = 0; }
错误原因分析
listName内存分配不足:initList中仅给listName分配1字节内存,但"PRO"/"CON"含终止符共需3字节,strcpy越界写入会直接破坏堆结构。addReason数组越界访问:当numReasons达到MAX_LIST_LENGTH-1时,执行list->numReasons++后数值变为MAX_LIST_LENGTH,此时list->reasons[list->numReasons] = NULL;会访问超出数组下标范围的元素(数组最大下标为MAX_LIST_LENGTH-1),引发堆损坏。- 内存泄漏隐患:当列表已满时,
addReason中分配的newReason未释放,造成内存泄漏。
修复方案
- 修正
listName内存分配:根据传入字符串的实际长度+1分配内存,确保能容纳终止符。 - 移除越界赋值操作:删除
addReason中的list->reasons[list->numReasons] = NULL;,numReasons已足够跟踪有效元素数量,无需修改越界位置。 - 处理满列表时的内存泄漏:列表已满时,先释放
newReason再返回。 - 优化
deleteList逻辑:释放所有初始化时分配的reasons元素内存(包括未使用的),避免内存泄漏。
修复后的完整代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #define PRO_OP 1 #define CON_OP 2 #define PRINT_OP 3 #define EXIT_OP 4 #define STR_LEN 50 #define MAX_LIST_LENGTH 10 typedef struct reasonList { char* listName; char* reasons[MAX_LIST_LENGTH]; int numReasons; } reasonList; void initList(reasonList* list, char* name); void addReason(reasonList* list); void printList(reasonList list); int menu(void); void myFgets(char str[], int n); void deleteList(reasonList* list); int main(void) { char dilemma[STR_LEN] = { 0 }; int op = 0; reasonList proList; initList(&proList, "PRO"); reasonList conList; initList(&conList, "CON"); printf("What is your dilemma?\n"); myFgets(dilemma, STR_LEN); while (op != EXIT_OP) { op = menu(); switch (op) { case(PRO_OP): addReason(&proList); break; case(CON_OP): addReason(&conList); break; case(PRINT_OP): printf("Your dilemma:\n"); printf("%s\n\n", dilemma); printList(proList); printList(conList); break; case(EXIT_OP): deleteList(&proList); deleteList(&conList); break; } } printf("Good luck!\n"); getchar(); return 0; } void initList(reasonList* list, char* listName) { // 根据字符串长度+1分配内存,确保容纳终止符 list->listName = (char*)malloc(strlen(listName) + 1); if (list->listName == NULL) { printf("no memory left\n"); exit(1); } strcpy(list->listName, listName); for (int i = 0; i < MAX_LIST_LENGTH; i++) { list->reasons[i] = (char*)malloc(sizeof(char) * STR_LEN); if (list->reasons[i] == NULL) { printf("no memory left\n"); exit(1); } strcpy(list->reasons[i], ""); } list->numReasons = 0; } void addReason(reasonList* list) { char* newReason = (char*)malloc(sizeof(char) * STR_LEN); if (newReason == NULL) { printf("no memory left\n"); exit(1); } printf("Enter a reason to add to the list %s:\n", list->listName); myFgets(newReason, STR_LEN); if (list->numReasons >= MAX_LIST_LENGTH) { // 释放newReason避免内存泄漏 free(newReason); return; } free(list->reasons[list->numReasons]); list->reasons[list->numReasons] = newReason; list->numReasons++; } void printList(reasonList list) { printf("list %s\n--------\n", list.listName); for (int i = 0; i < list.numReasons; i++) { printf("%s\n", list.reasons[i]); } printf("\n"); } int menu(void) { int op = 0; printf("Choose option:\n"); printf("%d - Add PRO reason\n", PRO_OP); printf("%d - Add CON reason\n", CON_OP); printf("%d - Print reasons\n", PRINT_OP); printf("%d - Exit\n", EXIT_OP); scanf("%d", &op); while (op < PRO_OP || op > EXIT_OP) { printf("Invalid option. Try again: "); scanf("%d", &op); } getchar(); return op; } void deleteList(reasonList* list) { // 释放所有reasons元素内存(包括未使用的) for (int i = 0; i < MAX_LIST_LENGTH; i++) { free(list->reasons[i]); list->reasons[i] = NULL; } free(list->listName); list->listName = NULL; } void myFgets(char str[], int n) { fgets(str, n, stdin); str[strcspn(str, "\n")] = 0; }
内容的提问来源于stack exchange,提问作者ni1
相关产品推荐
相关产品推荐

