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

移除else块后free()触发错误的原因是什么?

问题分析与修复

你遇到的核心问题是返回栈局部变量的地址导致的野指针访问,以下是具体分析和修复方案:


原代码

sedex.h

#ifndef KAAL_SEDEX_H
#define KAAL_SEDEX_H
#include "limits.h"
#ifdef ULLONG_MAX
#include "assert.h"
#include "crtdbg.h"
#include "stdio.h"
#include "stdlib.h"
#define MEMORY_UNINITIALIZED (size_t)(0xCCCCCCCCCCCCCCCC)
#define MEMORY_AVAILABLE(ptr) (assert(NULL != ptr && MEMORY_UNINITIALIZED != (size_t)(ptr)))

typedef char* string;
typedef long long llong;
typedef enum { true, false } bool;

typedef struct sedecimstruct {
    size_t length;
    llong* content;
} *sedex;

sedex CreateSedex(string source);
void DeleteSedex(sedex target);
#endif
#endif

sedex.c

#include "sedex.h"

int main(void)
{
    sedex n = CreateSedex("+");
    DeleteSedex(n);
    _CrtDumpMemoryLeaks();
    return 0;
}

sedex CreateSedex(string source)
{
    struct sedecimstruct output;
    bool sgned = source[0] == '+' || source[0] == '-';
    if (source == NULL || strlen(source) == 0 ||
        strlen(source) == 1 && sgned)
    {
        output.length = 2;
        output.content = calloc(output.length, sizeof(llong));
    }
    else
    {
        size_t digits = strlen(source) - sgned,
            mod = digits & 0xf;
        bool excess = mod > 0;
        output.length = (digits >> 4) + excess + 1;
        output.content = malloc(output.length * sizeof(llong));
        output.content[0] = source[0] == '-';
        char sedecim[17] = { 0 };
        MEMORY_AVAILABLE(output.content);
        for (size_t i = 1; i <= digits; i++)
        {
            sedecim[(i - 1) & 0xf] = source[i - 1 + sgned];
            if ((i & 0xf) == 0)
            {
                output.content[i >> 4] = atoll(sedecim);
                memset(sedecim, 0, sizeof(sedecim));
                continue;
            }
            if (i == digits && excess)
            {
                output.content[output.length - 1] = atoll(sedecim);
                memset(sedecim, 0, sizeof(sedecim));
            }
        }
    }
    return &output;
}

void DeleteSedex(sedex target)
{
    free(target->content); // breakpoint triggers here when else is empty or removed
    target->content = target->length = NULL;
}

问题根源

  1. 野指针的产生:CreateSedex函数里的struct sedecimstruct output是栈上的局部变量,函数执行完毕返回时,栈帧会被销毁,返回的&output变成指向无效内存的野指针。
  2. else块存在时的侥幸正常:当else块有内容时,函数执行过程中栈帧占用的空间更大,返回后栈内存被覆盖的速度较慢,野指针刚好还能访问到原来的内存区域,属于未定义行为的“巧合”;当else块被清空或移除后,函数执行完栈帧立即被后续操作覆盖,野指针指向的内存已经完全无效,调用free(target->content)时就会触发调试断点。

修复方案

把结构体从栈分配改为堆分配,确保返回的地址在函数结束后仍然有效:

修改后的CreateSedex函数

sedex CreateSedex(string source)
{
    // 堆分配结构体实例,确保返回后内存有效
    sedex output = malloc(sizeof(struct sedecimstruct));
    if (!output) {
        perror("malloc failed");
        return NULL;
    }

    bool sgned = source[0] == '+' || source[0] == '-';
    if (source == NULL || strlen(source) == 0 || strlen(source) == 1 && sgned)
    {
        output->length = 2;
        output->content = calloc(output->length, sizeof(llong));
    }
    else
    {
        size_t digits = strlen(source) - sgned,
            mod = digits & 0xf;
        bool excess = mod > 0;
        output->length = (digits >> 4) + excess + 1;
        output->content = malloc(output->length * sizeof(llong));
        if (!output->content) {
            perror("malloc content failed");
            free(output); // 结构体已分配,要先释放避免泄漏
            return NULL;
        }
        output->content[0] = source[0] == '-';
        char sedecim[17] = { 0 };
        MEMORY_AVAILABLE(output->content);
        for (size_t i = 1; i <= digits; i++)
        {
            sedecim[(i - 1) & 0xf] = source[i - 1 + sgned];
            if ((i & 0xf) == 0)
            {
                output->content[i >> 4] = atoll(sedecim);
                memset(sedecim, 0, sizeof(sedecim));
                continue;
            }
            if (i == digits && excess)
            {
                output->content[output->length - 1] = atoll(sedecim);
                memset(sedecim, 0, sizeof(sedecim));
            }
        }
    }
    return output;
}

修改后的DeleteSedex函数

void DeleteSedex(sedex target)
{
    if (target) {
        free(target->content); // 先释放content的堆内存
        free(target); // 再释放结构体本身的堆内存
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 14:37:33