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
相关产品推荐
相关产品推荐

