如何在C语言中从字符串数组弹出字符串且不触发段错误
动态字符串数组pop函数段错误问题分析与修复
核心问题分析
你的代码出现段错误的根源是对指针数组的内存管理逻辑混淆,主要体现在以下几点:
- 结构体变量用途混淆:
num_bytes被错误地同时承担"字符串总字节数"和"指针数组容量"的职责,导致内存分配计算完全错误。 - push函数内存分配错误:
size = sizeof(char *) * vec->num_bytes + length的计算逻辑毫无意义,直接导致指针数组的内存空间分配错误,后续移动元素时极易越界。 - pop函数内存分配错误:
realloc(vec->array, vec->num_bytes * sizeof(char))用字符串总长度乘以单个字符大小来分配指针数组的内存,完全不符合指针数组的内存需求,必然导致内存访问越界。 - pop函数内存泄漏:未释放被弹出的字符串,同时缩容逻辑错误。
- 结构体定义不完整:缺失结构体类型名,编译时会报错。
修正后的完整代码
首先重新定义结构体,区分指针数组的容量、活跃元素数量、字符串总字节数三个变量:
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct StringVector { char **array; // 存储字符串指针的数组 size_t active_length; // 当前活跃元素数量 size_t capacity; // 指针数组的容量(能容纳的指针数量) size_t total_bytes; // 所有字符串的总字节数(含终止符) } StringVector; int init_string_vector(StringVector *vec) { // 初始容量设为1 char **ptr = malloc(sizeof(char *)); if (ptr == NULL) { fprintf(stderr, "malloc failed in file %s at line %d\n", __FILE__, __LINE__); return -1; } vec->array = ptr; vec->active_length = 0; vec->capacity = 1; vec->total_bytes = 0; return 1; } int push_string_scalar_vec(StringVector *vec, const char *value, size_t index) { if (index > vec->active_length) { fprintf(stderr, "String index out of range in file %s on line %d\n", __FILE__, __LINE__); return -1; } // 复制字符串,自动处理终止符 char *dup_str = strdup(value); if (dup_str == NULL) { fprintf(stderr, "strdup failed in file %s at line %d\n", __FILE__, __LINE__); return -1; } size_t str_len = strlen(dup_str) + 1; // 包含终止符的长度 // 如果当前活跃元素数量等于容量,扩容(翻倍) if (vec->active_length == vec->capacity) { size_t new_capacity = vec->capacity * 2; char **resized = realloc(vec->array, new_capacity * sizeof(char *)); if (resized == NULL) { fprintf(stderr, "realloc failed in file %s at line %d\n", __FILE__, __LINE__); free(dup_str); return -1; } vec->array = resized; vec->capacity = new_capacity; } // 移动元素,为新元素腾出位置 for (size_t i = vec->active_length; i > index; i--) { vec->array[i] = vec->array[i-1]; } vec->array[index] = dup_str; vec->active_length += 1; vec->total_bytes += str_len; return 1; } void pop_string_scalar_vector(StringVector *vec, size_t index) { if (index >= vec->active_length) { fprintf(stderr, "Pop index out of range in file %s at line %d\n", __FILE__, __LINE__); return; } // 释放被弹出的字符串 free(vec->array[index]); size_t str_len = strlen(vec->array[index]) + 1; vec->total_bytes -= str_len; // 移动元素填补空缺 for (size_t i = index; i < vec->active_length - 1; i++) { vec->array[i] = vec->array[i + 1]; } vec->active_length -= 1; // 可选:当活跃元素数量小于容量的1/4时缩容,避免内存浪费 if (vec->active_length > 0 && vec->active_length <= vec->capacity / 4) { size_t new_capacity = vec->capacity / 2; char **resized = realloc(vec->array, new_capacity * sizeof(char *)); if (resized != NULL) { vec->array = resized; vec->capacity = new_capacity; } } } void free_string_vec(StringVector *vec) { if (vec == NULL) return; for (size_t i = 0; i < vec->active_length; i++) { free(vec->array[i]); } free(vec->array); vec->array = NULL; vec->active_length = 0; vec->capacity = 0; vec->total_bytes = 0; } int main() { StringVector vec; init_string_vector(&vec); push_string_scalar_vec(&vec, "One", vec.active_length); push_string_scalar_vec(&vec, "Two", vec.active_length); push_string_scalar_vec(&vec, "Three", vec.active_length); push_string_scalar_vec(&vec, "Four", vec.active_length); pop_string_scalar_vector(&vec, 2); // 测试输出剩余元素 for (size_t i = 0; i < vec.active_length; i++) { printf("%s\n", vec.array[i]); } free_string_vec(&vec); return 0; }
关键修复点说明
- 结构体拆分变量:新增
capacity变量专门记录指针数组的容量,total_bytes仅用于统计字符串总字节数,职责清晰。 - push函数扩容逻辑:当活跃元素数量等于容量时,将容量翻倍,确保指针数组有足够空间存储新元素。
- pop函数内存管理:先释放被弹出的字符串,再移动元素,缩容逻辑仅在必要时触发(避免频繁realloc影响性能)。
- 移除冗余逻辑:删除了无效的字符串终止符检查,
strdup会自动处理合法输入的字符串终止符。 - 边界检查:在pop函数中增加了索引越界检查,避免非法访问。
内容的提问来源于stack exchange,提问作者Jon
相关产品推荐
相关产品推荐

