You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 15:05:34