C语言文件与链表操作:调用openBook后addContact崩溃求助
看起来你遇到的崩溃问题根源在于内存分配的错误,而且这个错误的触发和堆内存的布局变化有关——单独调用addContact()时,堆的初始布局可能刚好没让越界访问立刻崩溃,但打开文件(哪怕只是打开再关闭)会改变堆的状态,导致越界操作直接触发程序崩溃。
核心错误:错误的内存分配大小
在addContact()函数里,你写了:
ite->next = (NODE*)malloc(sizeof(NODE*)); // 还有这行 head = (NODE*)malloc(sizeof(NODE*));
这里的问题是sizeof(NODE*)获取的是指针类型的大小(通常是4或8字节,取决于系统),但你需要分配的是整个NODE结构体的内存空间。结构体NODE包含多个字符数组,大小远大于指针,这样分配的内存严重不足,当你用scanf往里面写入数据时,会直接越界写入,破坏堆的完整性,导致后续操作崩溃。
正确的写法应该是分配sizeof(NODE):
ite->next = (NODE*)malloc(sizeof(NODE)); head = (NODE*)malloc(sizeof(NODE));
其他潜在问题修复
除了这个核心错误,你的代码还有几个需要修正的地方,避免后续出现其他问题:
1. feof()的错误使用
在openBook()里,你用!feof(read)作为循环条件,这会导致最后一次读取重复或者读取错误的数据(因为feof是在读取失败后才会置位)。正确的做法应该是把fscanf的返回值作为循环条件:
// 替换原来的读取逻辑 NODE *current = NULL; int result; while ((result = fscanf(read, "%s%s%s%s", name, email, ad, phone)) == 4) { NODE *new_node = (NODE*)malloc(sizeof(NODE)); strcpy(new_node->name, name); strcpy(new_node->email, email); strcpy(new_node->ad, ad); strcpy(new_node->phone, phone); new_node->next = NULL; if (head == NULL) { head = new_node; current = head; } else { current->next = new_node; current = new_node; } }
这样可以确保只有当成功读取4个字段时才创建节点,避免无效数据被加入链表。
2. fflush(stdin)的未定义行为
C标准里,fflush(stdin)是未定义行为,不同编译器处理方式不同。如果你想清除输入缓冲区的换行符,可以用如下方式替代:
// 替换fflush(stdin) int c; while ((c = getchar()) != '\n' && c != EOF);
3. 内存泄漏风险
程序结束前没有释放链表的内存,虽然是小问题,但养成好习惯可以避免内存泄漏。可以添加一个freeList()函数:
void freeList() { NODE *temp; while (head != NULL) { temp = head; head = head->next; free(temp); } }
然后在main()的return 0;前调用freeList();。
修复后的完整代码
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct node { char name[50]; char email[50]; char ad[200]; char phone[11]; struct node *next; } NODE; NODE *head = NULL; void openBook() { FILE *read = fopen("Book.txt", "r"); if (read == NULL) { printf("Failed to open Book.txt\n"); return; } char name[50] = "", email[50] = "", ad[200] = "", phone[11] = ""; NODE *current = NULL; int result; while ((result = fscanf(read, "%s%s%s%s", name, email, ad, phone)) == 4) { NODE *new_node = (NODE*)malloc(sizeof(NODE)); if (new_node == NULL) { printf("Memory allocation failed\n"); fclose(read); return; } strcpy(new_node->name, name); strcpy(new_node->email, email); strcpy(new_node->ad, ad); strcpy(new_node->phone, phone); new_node->next = NULL; if (head == NULL) { head = new_node; current = head; } else { current->next = new_node; current = new_node; } } fclose(read); } void clearInputBuffer() { int c; while ((c = getchar()) != '\n' && c != EOF); } void addContact() { NODE *ite = head; NODE *new_node = (NODE*)malloc(sizeof(NODE)); if (new_node == NULL) { printf("Memory allocation failed\n"); return; } new_node->next = NULL; if (head != NULL) { while (ite->next != NULL) ite = ite->next; ite->next = new_node; } else { head = new_node; } ite = new_node; printf("Enter name (no space): "); scanf("%s", ite->name); clearInputBuffer(); printf("Enter email : "); scanf("%s", ite->email); clearInputBuffer(); printf("Enter address : "); scanf("%s", ite->ad); clearInputBuffer(); printf("Enter phone : "); scanf("%s", ite->phone); clearInputBuffer(); } void printList() { NODE *iterator = head; if (iterator == NULL) { printf("Contact list is empty\n"); return; } while (iterator != NULL) { printf("Name: %s\nEmail: %s\nAddress: %s\nPhone: %s\n\n", iterator->name, iterator->email, iterator->ad, iterator->phone); iterator = iterator->next; } } void freeList() { NODE *temp; while (head != NULL) { temp = head; head = head->next; free(temp); } } int main() { openBook(); addContact(); printList(); freeList(); return 0; }
为什么单独调用addContact()没事?
这是因为堆内存的初始状态下,你分配的过小内存后面可能刚好是未使用的空间,越界写入暂时没有破坏关键的堆结构。但当你打开文件后,fopen和fclose会进行一些堆内存操作,改变了堆的布局,此时越界写入直接覆盖了堆的管理信息,导致程序崩溃。
内容的提问来源于stack exchange,提问作者mohammad mozafari

