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

C语言调用free释放堆对象却触发非堆对象警告问题排查

问题

我在用C语言编写简易解析器作业,复用过往代码。现有Token结构体及相关函数中,get_token生成的Token的lexeme指向堆内存可正常使用。为从文件读取Token,我编写allocate_token函数为lexeme分配堆内存,但在free_token函数中调用free(&token->lexeme)时,CLion提示“attempting to call free on non-heap object 'lexeme'”警告。我认为lexeme是堆分配指针,想了解问题原因或是否可忽略该警告。

相关代码

Tokens

typedef struct token {
    const char *lexeme;   //the actual string value of the token
    Category category;  //the category of the token
} Token;

Token create_token(const char *lexeme, Category cat) {
    Token token = {.lexeme = lexeme, .category = cat};
    return token;
}

Automatas

typedef struct automata {
    //does not directly store token but is easy to retrieve
    Category token_type;    //which type of token is the automata supposed to scan
    char *scanned;  //char buffer to store read characters from a string. Not realloced because it's been given reasonable capacity

    int lexeme_capacity;    //capacity of scanned buffer. Dynamic change is not implemented, all tokens should be at most this length
    ...
} Automata;

Automata create_automata(int num_states, char *accepted_chars,
                         int num_accepted_states, const int *accepted_states,
                         Category token_type) {
    ...
    //create and return an automata
    Automata automata = {
            .token_type = token_type,
            //TODO malloc returns an allocated pointer so it is not null but it must take into account possible overflows
            .scanned = malloc(DEFAULT_LEXEME_LENGTH * sizeof(char)),
            .lexeme_capacity = DEFAULT_LEXEME_LENGTH,
            ...
    };

    return automata;
}


Token get_token(Automata *automata) {
    // if automata is not in accepting state, it did not recognize the lexeme
    Category category = accept(automata) ? automata->token_type : CAT_NONRECOGNIZED;

    // easy to understand if written like this
    Token value = {
            .lexeme = automata->scanned,
            .category = category,
    };

    return value;
}

新增函数

Token allocate_token(const char *lexeme, size_t lexeme_len, Category cat) {
    //allocate and copy into heap memory the contents of the lexeme string
    char *heap_allocated = calloc(lexeme_len + 1, sizeof(char));
    memcpy(heap_allocated, lexeme, lexeme_len * sizeof(char));

    //create and return a token
    return create_token(heap_allocated, cat);
}

void free_token(Token *token) {
    free(&token->lexeme);
}

原因与解决方法

问题原因

你搞错了free的对象:token->lexeme本身是指向堆内存的指针,而&token->lexeme是这个指针变量自身的内存地址——这个地址属于栈内存(只要Token实例是在栈上创建或作为函数参数传递),并非堆内存,所以CLion会警告你试图释放非堆对象。

解决方法

直接释放token->lexeme指向的堆内存即可,不需要取地址。由于lexeme是const char*类型,显式转换为void*可以避免编译器可能出现的警告:

void free_token(Token *token) {
    // 显式转换为void*,消除const带来的类型警告
    free((void*)token->lexeme);
    // 将lexeme置为NULL,防止后续误操作野指针
    token->lexeme = NULL;
}

额外注意事项

  • 仅当token->lexeme确实指向堆内存时,才能调用free_token。如果有Token的lexeme指向字符串字面量或栈内存,调用该函数会触发未定义行为。
  • 可以考虑在Token结构体中添加一个布尔标记(如bool is_heap_allocated),记录lexeme的内存来源,避免误释放。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 00:55:05