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

C语言栈实现中realloc使用错误导致0xC0000374崩溃排查

C语言栈实现的内存错误排查与修复

我尝试用C语言实现一个栈,包含入栈(dodaj_do_stosu)、出栈(usun_ze_stosu)和查看栈顶的功能。程序编译后前两个元素可正常运行,但之后返回0xC0000374错误。我怀疑是realloc使用错误或结构体sizeof计算有误,请求帮忙排查代码,重点关注dodaj_do_stosu函数。我的代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

typedef struct {
    int n;
    char *nazwa;
}element_t;

typedef struct {
    int rozmiar;
    element_t **tablica;
}stos_t;


void top_of_stack(stos_t *s){
    printf("ostatni element stosu:rozmiar = %d nazwa=%s, n=%d\n", s->rozmiar, s->tablica[s->rozmiar]->nazwa, s->tablica[s->rozmiar]->n);
}
void init(stos_t *s)
{
    s->rozmiar=0;
    s->tablica=malloc(0);
}

void dodaj_do_stosu(stos_t *s, int n, char *name)
{

    s->tablica = realloc(s->tablica, (s->rozmiar + 1) * sizeof(s->tablica));
    s->tablica[s->rozmiar]->nazwa = name;
    s->tablica[s->rozmiar]->n = n;
    printf("rozmiar=%d, n=%d , nazwa=%s\n",s->rozmiar, s->tablica[s->rozmiar]->n, s->tablica[s->rozmiar]->nazwa);
    s->rozmiar++;


}

void usun_ze_stosu(stos_t *s)
{
    s->tablica = realloc(s->tablica, (s->rozmiar - 1) * sizeof(s->tablica[0]));
    s->rozmiar--;
}

void rm(stos_t s)
{
    free(s.tablica);
}

int main(int argc, char **argv)
{
    stos_t s;
    init(&s);
    int i;
    srand(time(0));
    if (argc>1)
        for(i=1;i<argc;i++){
            printf("%s\n", argv[i]);
            dodaj_do_stosu(&s, rand() % 10, argv[i]);
        }
    for(i=0;i<argc-1;i++){
        //printf("i=%d, n=%d, nazwa=%s\n",i, s.tablica[i].n, s.tablica[i].nazwa);
    }

    //top_of_stack(&s);
    //usun_ze_stosu(&s);
    //top_of_stack(&s);
    rm(s);
    return 0;

}

核心错误点分析

1. dodaj_do_stosu中内存分配的sizeof计算错误

你用了sizeof(s->tablica),但s->tablica是element_t**类型(指针的指针),这个sizeof得到的是指针本身的大小(比如32位系统4字节,64位8字节)。虽然数值上和element_t*的大小一致,但逻辑错误,且如果后续类型变更容易出问题。正确写法应该用sizeof(element_t*)或者sizeof(s->tablica[0]),后者更健壮。

2. 未为element_t实例分配内存

s->tablica[s->rozmiar]是未初始化的野指针,直接访问它的nazwa和n成员会触发非法内存访问。每次入栈时,必须先为新的element_t分配内存,再进行赋值操作。

3. top_of_stack的栈顶索引错误

栈的rozmiar是元素个数,索引从0开始,栈顶元素的索引应该是s->rozmiar - 1,而不是s->rozmiar,后者会访问超出数组范围的内存,直接触发越界错误。

4. init函数中malloc(0)的隐患

虽然部分编译器允许malloc(0)返回可被free的指针,但这种写法不规范,realloc对NULL的处理更可靠,建议初始化为s->tablica = NULL。

5. usun_ze_stosu的边界判断缺失

当栈为空(rozmiar == 0)时,调用该函数会让realloc的大小为-1(转换为无符号数后是极大值),直接触发内存错误,必须先判断rozmiar > 0再执行缩容。


修正后的完整代码

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

typedef struct {
    int n;
    char *nazwa;
} element_t;

typedef struct {
    int rozmiar;
    element_t **tablica;
} stos_t;

void top_of_stack(stos_t *s) {
    if (s->rozmiar == 0) {
        printf("Stos jest pusty!\n");
        return;
    }
    element_t *top = s->tablica[s->rozmiar - 1];
    printf("Ostatni element stosu: rozmiar = %d, nazwa=%s, n=%d\n", s->rozmiar, top->nazwa, top->n);
}

void init(stos_t *s) {
    s->rozmiar = 0;
    s->tablica = NULL; // 初始化为NULL,realloc处理更可靠
}

void dodaj_do_stosu(stos_t *s, int n, char *name) {
    // 扩容:使用sizeof(s->tablica[0])确保类型正确
    element_t **new_tab = realloc(s->tablica, (s->rozmiar + 1) * sizeof(s->tablica[0]));
    if (new_tab == NULL) {
        perror("realloc failed");
        exit(EXIT_FAILURE);
    }
    s->tablica = new_tab;

    // 为新元素分配内存
    s->tablica[s->rozmiar] = malloc(sizeof(element_t));
    if (s->tablica[s->rozmiar] == NULL) {
        perror("malloc failed");
        exit(EXIT_FAILURE);
    }

    // 赋值
    s->tablica[s->rozmiar]->nazwa = name;
    s->tablica[s->rozmiar]->n = n;
    printf("rozmiar=%d, n=%d , nazwa=%s\n", s->rozmiar, s->tablica[s->rozmiar]->n, s->tablica[s->rozmiar]->nazwa);
    s->rozmiar++;
}

void usun_ze_stosu(stos_t *s) {
    if (s->rozmiar == 0) {
        printf("Nie mozna usunac elementu z pustego stosu!\n");
        return;
    }
    // 先释放栈顶元素的内存
    free(s->tablica[s->rozmiar - 1]);
    // 缩容栈的指针数组
    element_t **new_tab = realloc(s->tablica, (s->rozmiar - 1) * sizeof(s->tablica[0]));
    if (new_tab != NULL || s->rozmiar - 1 == 0) { // 缩容到0时realloc返回NULL是合法的
        s->tablica = new_tab;
    }
    s->rozmiar--;
}

void rm(stos_t *s) { // 改为传指针,避免拷贝,同时释放所有元素内存
    for (int i = 0; i < s->rozmiar; i++) {
        free(s->tablica[i]);
    }
    free(s->tablica);
    s->tablica = NULL;
    s->rozmiar = 0;
}

int main(int argc, char **argv) {
    stos_t s;
    init(&s);
    int i;
    srand(time(0));

    if (argc > 1) {
        for (i = 1; i < argc; i++) {
            printf("%s\n", argv[i]);
            dodaj_do_stosu(&s, rand() % 10, argv[i]);
        }
    }

    top_of_stack(&s);
    usun_ze_stosu(&s);
    top_of_stack(&s);

    rm(&s);
    return 0;
}

额外优化说明

  • 增加了内存分配失败的错误处理(perror和exit),避免程序静默崩溃
  • rm函数改为传指针,先释放每个element_t的内存再释放指针数组,彻底避免内存泄漏
  • 出栈时先释放栈顶元素内存再缩容指针数组,防止内存泄漏
  • 所有栈操作增加空栈判断,提升程序健壮性

内容的提问来源于stack exchange,提问作者Michał Jagodzinski

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 13:20:31