将char*赋值给void*字段后,链表队列存文件行重复问题求助
问题与解决方法
问题描述
编写读取文件行并存储到链表队列的程序,队列节点包含void*类型的data字段。打印存储的所有行时,程序重复输出最后读取的行,重复次数等于文件行数。推测原因是每次向节点传递的都是同一指针,读取新行时指针指向的内容被覆盖,求解决办法。
相关代码
文件读取函数
void* readfile(void* arg){ FILE* fp; fp = fopen((char*)arg, "r"); if(fp==NULL){ fprintf(stderr, "Error opening file %s\n", (char*)arg); exit(EXIT_FAILURE); } char* line= malloc(sizeof(char)*BUFSIZE); // #define BUFSIZE 1024 while(fgets(line, BUFSIZE, fp)!=NULL){ insert_tail(deque_12, (void*)line); } puts("File read"); fclose(fp); return NULL; }
队列结构与实现(deque.h)
typedef struct node{ void* data; struct node *next; struct node *prev; }Node_t; typedef struct deque{ Node_t *head; Node_t *tail; }Deque_t; void insert_tail(Deque_t* d, void* data){ Node_t *newNode = malloc(sizeof(Node_t*)); newNode->data = data; newNode->next = NULL; if(d->head == NULL){ newNode->prev = NULL; d->head = newNode; } else{ newNode->prev = d->tail; d->tail->next = newNode; } d->tail = newNode; }
解决办法
你的推测完全正确:所有节点的data字段都指向同一块内存(line指针指向的缓冲区),每次fgets都会覆盖这块内存的内容,最终所有节点都指向最后一行的内容。此外,代码里还有一个隐藏问题:insert_tail中malloc(sizeof(Node_t*))分配的内存大小错误,应该是malloc(sizeof(Node_t)),否则会因内存不足导致未定义行为。
具体修改步骤
- 为每行内容分配独立内存:每次读取一行后,复制该行内容到新分配的内存中,再将新内存的指针传入队列。
- 修复节点内存分配错误:将
malloc(sizeof(Node_t*))改为malloc(sizeof(Node_t))。
修改后的代码示例
修改后的readfile函数
void* readfile(void* arg){ FILE* fp; fp = fopen((char*)arg, "r"); if(fp==NULL){ fprintf(stderr, "Error opening file %s\n", (char*)arg); exit(EXIT_FAILURE); } char line[BUFSIZE]; // 用栈缓冲区替代堆分配,减少内存管理开销 while(fgets(line, BUFSIZE, fp)!=NULL){ // 为当前行分配内存并复制内容 char* line_copy = malloc(strlen(line) + 1); if(line_copy == NULL){ fprintf(stderr, "Memory allocation failed\n"); exit(EXIT_FAILURE); } strcpy(line_copy, line); insert_tail(deque_12, (void*)line_copy); } puts("File read"); fclose(fp); return NULL; }
修改后的insert_tail函数
void insert_tail(Deque_t* d, void* data){ // 分配Node_t结构体大小的内存,而非指针大小 Node_t *newNode = malloc(sizeof(Node_t)); if(newNode == NULL){ fprintf(stderr, "Memory allocation for node failed\n"); exit(EXIT_FAILURE); } newNode->data = data; newNode->next = NULL; if(d->head == NULL){ newNode->prev = NULL; d->head = newNode; } else{ newNode->prev = d->tail; d->tail->next = newNode; } d->tail = newNode; }
注意事项
- 后续释放队列节点时,需要先释放
data指向的内存,再释放节点本身,避免内存泄漏。 - 若文件行长度超过
BUFSIZE,fgets会截断行内容,可根据需求调整BUFSIZE或实现动态扩容的行读取逻辑。
内容的提问来源于stack exchange,提问作者DravStart
相关产品推荐
相关产品推荐

