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

malloc后调用memset触发断言失败,添加getchar恢复正常的原因咨询

问题描述

定义了如下结构体:

struct state {
    bool isfinal;
    bool *isfull;
    char **board;
};

其中isfull是一维数组,board是二维数组。编写了如下内存分配函数:

初始版本(运行崩溃)

struct state new_state(struct state state)
{
    int i;
    struct state new_state = {};
    new_state.isfull = (bool *)malloc(BOARD_WIDTH * sizeof(bool));
    new_state.board = (char **)malloc(BOARD_HIGHT * sizeof(char *));
    for (i = 0; i < BOARD_HIGHT; ++i)
        new_state.board[i] = (char *)malloc(BOARD_WIDTH * sizeof(char));
    if (state.board) {
        memcpy(new_state.isfull, state.isfull, BOARD_WIDTH * sizeof(bool));
        for (i = 0; i < BOARD_HIGHT; ++i)
            memcpy(new_state.board[i], state.board[i], BOARD_WIDTH * sizeof(char));
    } else {
        memset(new_state.isfull, 0, BOARD_WIDTH * sizeof(bool));
        for (i = 0; i < BOARD_HIGHT; ++i)
            memset(new_state.board[i], 0, BOARD_WIDTH * sizeof(char *));
    }
    return new_state;
}

首次调用时传入的state.board为0,执行else分支后触发断言失败,且函数执行到return后程序崩溃。

修复版本1:替换malloc为calloc(问题解决)

struct state new_state(struct state state)
{
    int i;
    struct state new_state = {};
    new_state.isfull = (bool *)calloc(BOARD_WIDTH, sizeof(bool));
    new_state.board = (char **)calloc(BOARD_HIGHT, sizeof(char *));
    for (i = 0; i < BOARD_HIGHT; ++i)
        new_state.board[i] = (char *)calloc(BOARD_WIDTH, sizeof(char));
    if (state.board) {
        memcpy(new_state.isfull, state.isfull, BOARD_WIDTH * sizeof(bool));
        for (i = 0; i < BOARD_HIGHT; ++i)
            memcpy(new_state.board[i], state.board[i], BOARD_WIDTH * sizeof(char));
    } else {
        // memset(new_state.isfull, 0, BOARD_WIDTH * sizeof(bool));
        // for (i = 0; i < BOARD_HIGHT; ++i)
        //     memset(new_state.board[i], 0, BOARD_WIDTH * sizeof(char *));
    }
    return new_state;
}

奇怪现象:添加getchar()延迟后也能正常运行

struct state new_state(struct state state)
{
    int i;
    struct state new_state = {};
    new_state.isfull = (bool *)malloc(BOARD_WIDTH * sizeof(bool));
    new_state.board = (char **)malloc(BOARD_HIGHT * sizeof(char *));
    for (i = 0; i < BOARD_HIGHT; ++i)
        new_state.board[i] = (char *)malloc(BOARD_WIDTH * sizeof(char));
    if (state.board) {
        memcpy(new_state.isfull, state.isfull, BOARD_WIDTH * sizeof(bool));
        for (i = 0; i < BOARD_HIGHT; ++i)
            memcpy(new_state.board[i], state.board[i], BOARD_WIDTH * sizeof(char));
    } else {
        getchar();
        memset(new_state.isfull, 0, BOARD_WIDTH * sizeof(bool));
        for (i = 0; i < BOARD_HIGHT; ++i)
            memset(new_state.board[i], 0, BOARD_WIDTH * sizeof(char *));
    }
    return new_state;
}

已知malloc后调用memset是常规操作,提出两个问题:

  1. 第一个版本的函数为何无法正常运行?
  2. 添加getchar()为何能解决问题?

问题解答

1. 初始版本崩溃的原因

问题出在else分支的memset参数错误:

memset(new_state.board[i], 0, BOARD_WIDTH * sizeof(char *));

new_state.board[i]指向的是BOARD_WIDTH个char的内存块,每个char占1字节,所以memset需要设置的字节数应该是BOARD_WIDTH * sizeof(char),但代码里错误地写成了BOARD_WIDTH * sizeof(char*)。

sizeof(char*)在32位系统是4字节、64位系统是8字节,远大于sizeof(char),这直接导致缓冲区溢出,破坏了堆内存的内部结构。这种错误的后果具有延迟性,可能在函数返回后,后续内存操作触发堆结构校验时才会崩溃。

calloc版本正常的原因是:calloc本身会将分配的内存初始化为0,且你注释掉了错误的memset代码,从根源上避免了溢出问题。

2. 添加getchar()后正常运行的原因

这是未定义行为的典型表现:
缓冲区溢出后,堆内存的损坏位置可能暂时没有被其他关键数据结构占用;添加getchar()后,程序暂停等待输入,期间没有额外的内存操作,堆的损坏没有被触发。或者等待过程中,操作系统的内存调度、堆管理的内部状态发生变化,刚好让损坏区域未影响到后续执行。

这只是巧合,不是真正的修复——内存损坏依然存在,换个运行环境、输入时机,程序大概率还是会崩溃。

内容的提问来源于stack exchange,提问作者amir z

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 05:28:13