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

C语言take_quotes内存释放致tokens失效及Valgrind报错问题问询

解决方案

问题根源

  • strtok不会生成新的子字符串,仅会修改传入的原内存块,返回的指针均指向take_quotes分配的str内存内部,因此提前释放str会导致tokens数组内所有指针变为悬空指针,访问直接失效。
  • 你改用strdup复制子串的思路是正确的,Valgrind报内存泄漏来自三个问题:
    1. 异常分支未做完整内存清理:循环中realloc分配失败时,原代码仅释放了tokens指针数组,没有释放已经用strdup分配的各个子串,也没有释放str本身。
    2. 调用端未遵循释放规则:tokenize返回的tokens数组需要按固定规则释放,调用方未做对应操作也会导致内存泄漏。
    3. 缺少对take_quotes返回空的判断:如果take_quotes申请内存失败返回NULL,后续strtok会直接触发空指针访问。

修正后代码

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

// 非POSIX环境可以替换为自定义的strdup实现
static char* my_strdup(const char* s) {
    if (!s) return NULL;
    size_t len = strlen(s) + 1;
    char* dup = malloc(len);
    if (dup) memcpy(dup, s, len);
    return dup;
}

char** tokenize(char* line){
    if(line == NULL)
        return NULL;

    char* str = take_quotes(line, stderr);
    // 新增take_quotes返回空的异常处理
    if (str == NULL)
        return NULL;

    char **tokens = malloc( sizeof( char * ) );
    *tokens = NULL;
    size_t n = 1;

    const char *delim = " \t \n";

    char *p = strtok( str, delim );

    int success = p != NULL;

    while ( success )
    {
        char **tmp = realloc( tokens, ( n + 1 ) * sizeof( char * ) );

        if ( tmp == NULL )
        {
            // 新增清理逻辑:释放已经分配的子串
            for (size_t i = 0; tokens[i]; i++) {
                free(tokens[i]);
            }
            free( tokens );
            tokens = NULL;
            success = 0;
        }
        else
        {
            tokens = tmp;
            // 复制子串,和原str内存脱离依赖
            tokens[n - 1] = my_strdup(p);
            // 处理strdup分配失败的情况
            if (tokens[n-1] == NULL) {
                // 清理已经分配的资源
                for (size_t i = 0; i < n-1; i++) {
                    free(tokens[i]);
                }
                free(tokens);
                tokens = NULL;
                success = 0;
                break;
            }
            tokens[n] = NULL;
            ++n;

            p = strtok( NULL, delim );

            success = p != NULL;
        }
    }

    // 现在释放str不会影响tokens里的内容
    free(str);
    str = NULL;

    return tokens;
}

调用端正确释放方式

返回的tokens数组以NULL作为结束标记,使用完后需要先逐个释放每个子串,再释放指针数组本身:

// 调用示例
char** tokens = tokenize("Hello \"nice\" day");
if (tokens != NULL) {
    // 这里写使用tokens的业务逻辑
    for (size_t i = 0; tokens[i] != NULL; i++) {
        printf("token %zu: %s\n", i, tokens[i]);
    }

    // 释放逻辑
    for (size_t i = 0; tokens[i] != NULL; i++) {
        free(tokens[i]); // 先释放每个子字符串
    }
    free(tokens); // 最后释放指针数组
}

按照以上逻辑修改后,take_quotes分配的内存可以在tokenize内部正常释放,也不会出现内存泄漏问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 21:45:03