Realloc触发SIGABRT问题排查:图路径规划程序崩溃求助
让我们一步步拆解你的问题,先找到触发SIGABRT的根源,再给出修复方案和优化建议。
为什么会触发SIGABRT?
SIGABRT在这里的核心原因是内存越界操作破坏了堆的元数据——malloc/realloc的底层机制检测到堆损坏后,会调用abort()终止程序。你的代码里有几个关键的内存操作错误:
1. 首次扩容时的内存大小计算错误
在处理v1->neighbors[0] == NULL的分支中,你写了:
real = (Vertex**) realloc(v1->neighbors, sizeof(Vertex*)+1);
sizeof(Vertex*)+1表示「一个指针的大小 + 1字节」,但你需要存储两个指针(新邻居 + NULL终止符),正确的大小应该是2 * sizeof(Vertex*)。这个错误会导致分配的内存空间不足,后续写入v1->neighbors[1] = NULL时直接越界,破坏堆的内部结构。
2. 处理v2空邻居分支的双重错误
在处理v2->neighbors[0] == NULL的分支中:
- 内存大小计算错误:你用
sizeof(Vertex*)申请内存,但之后要写入v2->neighbors[1] = NULL,需要至少2个指针的空间,应该是2 * sizeof(Vertex*)。 - 未定义变量:
v2->neighbors = realocado;中的realocado是拼写错误,应该是real。这个错误会导致未定义行为,直接将非法地址赋值给v2->neighbors,进一步破坏堆。
3. 邻居计数循环的冗余与潜在风险
计算邻居数量的循环里,aux = v1->neighbors[i];是冗余的(因为初始aux已经是v1->neighbors[0]),而且如果堆已经被破坏,邻居列表的NULL终止符被覆盖,循环会无限执行,导致i值过大,申请超出系统限制的内存,最终触发崩溃。
修复后的代码
以下是修正了所有上述问题的版本:
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct vertex Vertex; struct vertex { char* name; Vertex** neighbors; float x, y; }; // 假设你已经实现了areNeighbors函数 int areNeighbors(Vertex* v1, Vertex* v2) { if (v1->neighbors[0] == NULL) return 0; int i = 0; while (v1->neighbors[i] != NULL) { if (v1->neighbors[i] == v2) return 1; i++; } return 0; } Vertex* create_vertex(char* name, float x, float y) { Vertex* vertex = (Vertex*) malloc(sizeof(Vertex)); if (!vertex) { printf("Failed to allocate vertex\n"); return NULL; } vertex->x = x; vertex->y = y; vertex->name = (char*) malloc(strlen(name) + 1); if (!vertex->name) { free(vertex); printf("Failed to allocate vertex name\n"); return NULL; } strcpy(vertex->name, name); // 初始分配1个指针的空间(存储NULL) vertex->neighbors = (Vertex**) malloc(sizeof(Vertex*)); if (!vertex->neighbors) { free(vertex->name); free(vertex); printf("Failed to allocate neighbors list\n"); return NULL; } vertex->neighbors[0] = NULL; return vertex; } void creates_connection(Vertex* v1, Vertex* v2) { if (areNeighbors(v1, v2)) { printf("Vertices ja sao vizinhos\n"); return; } // 处理v1的邻居列表 if (v1->neighbors[0] == NULL) { // 需要2个指针的空间:v2 + NULL Vertex** real = (Vertex**) realloc(v1->neighbors, 2 * sizeof(Vertex*)); if (real != NULL) { v1->neighbors = real; v1->neighbors[0] = v2; v1->neighbors[1] = NULL; } else { printf("Nao foi possivel realocar memoria para v1\n"); } } else { // 计算当前邻居数量(包括NULL终止符的索引) int i = 0; while (v1->neighbors[i] != NULL) { i++; } // 扩容为i+2个指针:原有i个(i-1个邻居 + NULL) + 新邻居 + 新NULL Vertex** real = (Vertex**) realloc(v1->neighbors, (i + 1) * sizeof(Vertex*)); if (real != NULL) { v1->neighbors = real; v1->neighbors[i] = v2; v1->neighbors[i + 1] = NULL; } else { printf("Nao foi possivel realocar memoria para v1\n"); } } // 处理v2的邻居列表 if (v2->neighbors[0] == NULL) { Vertex** real = (Vertex**) realloc(v2->neighbors, 2 * sizeof(Vertex*)); if (real != NULL) { v2->neighbors = real; // 修正变量名错误 v2->neighbors[0] = v1; v2->neighbors[1] = NULL; } else { printf("Nao foi possivel realocar memoria para v2\n"); } } else { int i = 0; while (v2->neighbors[i] != NULL) { i++; } Vertex** real = (Vertex**) realloc(v2->neighbors, (i + 1) * sizeof(Vertex*)); if (real != NULL) { v2->neighbors = real; v2->neighbors[i] = v1; v2->neighbors[i + 1] = NULL; } else { printf("Nao foi possivel realocar memoria para v2\n"); } } }
优化建议
当前的实现每次添加邻居都需要遍历整个列表计数,效率较低,而且容易出错。建议给Vertex结构体添加一个neighbor_count字段,记录当前邻居的数量,避免遍历:
typedef struct vertex Vertex; struct vertex { char* name; Vertex** neighbors; float x, y; int neighbor_count; // 新增:记录当前邻居的数量(不包括NULL终止符) };
然后在create_vertex中初始化neighbor_count = 0,添加邻居时直接使用这个计数来计算内存大小:
// 在create_vertex中添加 vertex->neighbor_count = 0; // 添加邻居时的逻辑简化(以v1为例) if (v1->neighbor_count == 0) { Vertex** real = (Vertex**) realloc(v1->neighbors, 2 * sizeof(Vertex*)); if (real) { v1->neighbors = real; v1->neighbors[0] = v2; v1->neighbors[1] = NULL; v1->neighbor_count = 1; } } else { Vertex** real = (Vertex**) realloc(v1->neighbors, (v1->neighbor_count + 2) * sizeof(Vertex*)); if (real) { v1->neighbors = real; v1->neighbors[v1->neighbor_count] = v2; v1->neighbors[v1->neighbor_count + 1] = NULL; v1->neighbor_count++; } }
这样不仅提高了效率,还避免了遍历列表可能带来的错误,代码也更简洁。
内容的提问来源于stack exchange,提问作者yamunaq

