为何数组前后元素指向超作用域局部结构体时出现混乱?
栈上结构体变量超出作用域后的行为疑问
我在测试声明结构体变量和用malloc分配结构体两种方式的访问特性,知道栈上声明的结构体变量只在作用域内有效,超出后会被销毁。我写了下面的程序:
#include<stdio.h> #include <stdlib.h> typedef struct Edge { int edgeNum; struct Edge* next; }Edge; typedef struct Vertex { int vertexNum; Edge* next; }Vertex; void addEdge(Vertex* v, int edgeNum) { Edge* currentEdge; Edge newEdge; newEdge.edgeNum = edgeNum; if (v->next == NULL) { v->next = &newEdge; } else { currentEdge = v->next; while (currentEdge->next != NULL) currentEdge = currentEdge->next; currentEdge->next = &newEdge; //this part doesnt represent a real graph } newEdge.next = NULL; } int main() { int IG[7][14] = { {1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0} }; int i, j; Vertex v[7]; Edge *new_edge; Edge *current; for(i=0; i < 7; i++){ /* Set order number to each list */ v[i].vertexNum = i+1; /* Initialize the edge list with NULL */ v[i].next = NULL; for(j=0; j < 14; j++){ if( IG[i][j] == 1 ){ addEdge(&v[i],j+1); if (i>0) printf(" line %d: %d and line%d: %d\n",i-1,v[i-1].next->edgeNum, i, v[i].next->edgeNum); } } } return 0; }
我遍历矩阵每一行,遇到值为1的元素就创建一个Edge结构体关联对应顶点。我知道这么做有问题,因为结构体超出作用域会被销毁,但实际结果和预期不符——我原本以为循环结束后v[]数组的每个元素都应该指向NULL,因为结构体已经被销毁。
但实际情况是,后续创建的结构体似乎覆盖了前面元素的指向。循环结束后遍历数组,所有元素都指向最后一个元素对应的结构体:比如矩阵第二行唯一的1在2x1位置,对应的edgeNum应该是1,但实际变成了13(矩阵最后一行最后一个1的位置),其他行也都是这个情况。
希望有人解释我的预测错误在哪里,感谢。
问题原因解析
你的核心误解是栈上变量销毁后,指向它的指针会自动变成NULL——这完全是错误的。栈上变量超出作用域后,系统只是标记这块内存区域可以被后续栈操作复用,并不会主动把之前的指针置为NULL,也不会清空内存里的数据。
具体到你的代码:
addEdge函数里的newEdge是栈上局部变量,每次调用addEdge时,它都会被分配在栈的同一位置(因为函数调用栈的结构是固定的,每次进入函数都会在栈顶分配同样大小的空间)。- 你把
&newEdge赋值给v->next或者链表节点的next指针,本质上是让这些指针指向栈上的同一个固定地址。 - 每次调用
addEdge时,都会覆盖这个地址上的edgeNum值,最后一次调用时把它设为13,所以所有指针指向的内存区域里的edgeNum都是13。 - 当函数返回后,这块内存并没有被清空,只是变成了“可用”状态,所以你的指针依然能访问到这块内存里的最后一次写入的值,而不是变成NULL。
简单说:指针本身不会因为指向的内存失效而自动变为NULL,失效的栈内存会被后续操作覆盖,导致你看到的所有指针都指向最后一次写入的数据。
正确的做法是用malloc动态分配Edge结构体,这样内存会在堆上,不会随函数返回而失效,直到你用free释放。
内容的提问来源于stack exchange,提问作者Nathan
相关产品推荐
相关产品推荐

