链表实现队列时数据丢失与指针异常问题求助——修改节点数据类型后display输出全0
问题分析与修复方案
你的核心问题出在链表节点对数组的内存管理和指针操作错误,不管是传值还是传指针版本,都存在野指针、指针访问优先级错误或者内存赋值无效的问题。下面逐一拆解并给出修复方案:
1. 传值版本的致命错误
在enqueue函数里,你写了:
tmp->data = &value;
这里的value是函数的形参(数组作为函数参数会退化为指针),本质上是局部变量。当enqueue函数执行完毕后,这个局部变量的内存会被系统回收,tmp->data就变成了野指针。后续调用display访问这个指针时,内存内容已经被覆盖,所以输出全是0(或随机垃圾值)。
另外,你试图在dequeue里用free(tmp)释放数组,但实际上你根本没有为数组分配动态内存,这会导致非法内存释放操作。
2. 传指针版本的错误
- 指针访问优先级错误:在
enqueue里打印数组时,*value[i]的写法是错的。因为[]的优先级高于*,value[i]会被先解析为第i个float[20]数组的指针,这显然不是你要的。正确写法应该是(*value)[i],先解引用得到数组,再访问下标。 - 野指针隐患:你把
tmp->data指向了main里的局部数组tmp,后续如果修改main里的tmp,队列里的内容会跟着变化;如果局部数组内存被回收,同样会出现野指针问题。
3. 通用修复思路
链表节点需要独立存储每个数组的数据,而不是指向外部局部变量。这里推荐两种可靠方案:
方案一:节点直接存储数组(简单易维护)
修改节点结构体,直接在节点内定义数组,这样分配节点时会自动包含数组内存,避免额外的内存管理:
修正后的完整代码
#include <time.h> #include <stdio.h> #include <stdlib.h> #include <string.h> // 需要memcpy做数组拷贝 #define QUEUE_SIZE 30 typedef float dataArray[20]; struct node { dataArray data; // 直接存储数组,无需指针 struct node *next; }; typedef struct node node; struct queue { int count; node *front; node *rear; }; typedef struct queue queue; void initialize(queue *q); int is_empty(queue *q); void enqueue(queue *q, const dataArray value); void dequeue(queue *q, dataArray outputArray); void display(node *head); float random_float(); void initialize(queue *q) { q->count = 0; q->front = NULL; q->rear = NULL; } int is_empty(queue *q) { return (q->rear == NULL); } void enqueue(queue *q, const dataArray value) { // 队列满时先出队一个元素 if (q->count == QUEUE_SIZE) { dataArray tmp; dequeue(q, tmp); } node *tmp_node = malloc(sizeof(node)); if (!tmp_node) { // 检查内存分配是否成功 perror("malloc failed"); exit(EXIT_FAILURE); } // 拷贝数组内容到节点,确保数据独立 memcpy(tmp_node->data, value, sizeof(dataArray)); tmp_node->next = NULL; if (!is_empty(q)) { q->rear->next = tmp_node; q->rear = tmp_node; } else { q->front = q->rear = tmp_node; } q->count++; // 调试输出 printf(" contents of tmp-node in enqueue\n"); for (int i = 0; i < 20; i++) { printf("%f\n", tmp_node->data[i]); } } void dequeue(queue *q, dataArray outputArray) { if (is_empty(q)) { printf("Queue is empty!\n"); return; } node *tmp_node = q->front; // 把队列头部的数组拷贝到输出数组 memcpy(outputArray, tmp_node->data, sizeof(dataArray)); // 调试输出 printf("dequeue output before freeing memory\r\n===========\r\n"); for (int i = 0; i < 20; i++) { printf("%f\n", outputArray[i]); } q->front = q->front->next; if (q->front == NULL) { // 队列空了后重置rear q->rear = NULL; } q->count--; free(tmp_node); // 释放节点,数组作为节点的一部分自动被释放 } void display(node *head) { if (head == NULL) { printf("NULL\r\n"); return; } for (int i = 0; i < 20; i++) { printf("%f\n", head->data[i]); } display(head->next); } float random_float() { // srand只需要初始化一次,放在main里即可 float a = 50.0; return ((float)rand() / (float)(RAND_MAX)) * a; } int main() { queue *q = malloc(sizeof(queue)); if (!q) { perror("malloc failed"); exit(EXIT_FAILURE); } initialize(q); srand((unsigned int)time(NULL)); // 仅初始化一次随机种子 dataArray tmp; for (int i = 0; i < 20; i++) { tmp[i] = random_float(); } printf("display dataArray before enqueue\r\n===========\r\n"); for (int i = 0; i < 20; i++) { printf("%f\n", tmp[i]); } enqueue(q, tmp); printf("Queue display after enqueue\r\n===========\r\n"); display(q->front); printf("Queue before dequeue\r\n===========\r\n"); printf("Queue #1 element count: %d\r\n", q->count); dataArray queData; dequeue(q, queData); printf("Queue after dequeue\r\n===========\r\n"); printf("Queue #1 element count: %d\r\n", q->count); for (int i = 0; i < 20; i++) { printf("%f\n", queData[i]); } // 清理队列剩余节点 while (!is_empty(q)) { dataArray dummy; dequeue(q, dummy); } free(q); return 0; }
关键修复点
- 节点结构体修改:把
dataArray *data改成dataArray data,每个节点自带独立的数组空间。 - 数组拷贝:用
memcpy复制数组内容,确保队列中每个元素的数据独立。 - dequeue函数修正:不再试图修改形参指针(之前的赋值是无效的,因为形参是局部变量),而是直接拷贝数据到输出数组。
- 随机种子优化:把
srand移到main中仅调用一次,避免每次生成相同的随机数。 - 内存安全:添加内存分配失败检查和空队列处理,避免程序崩溃。
方案二:动态分配数组(适合内存敏感场景)
如果需要灵活控制数组内存,可以让节点指针指向动态分配的数组,但要注意双重内存管理:
// 修改节点结构体 struct node { dataArray *data; // 指向动态分配的数组 struct node *next; }; // enqueue中分配数组 tmp_node->data = malloc(sizeof(dataArray)); memcpy(tmp_node->data, value, sizeof(dataArray)); // dequeue中释放内存 free(tmp_node->data); // 先释放数组 free(tmp_node); // 再释放节点
内容的提问来源于stack exchange,提问作者mech
相关产品推荐
相关产品推荐

