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

自定义类strtok函数返回值异常:main中无法打印token

问题根源:返回指向栈局部变量的指针导致悬空指针

你遇到的核心问题是返回了指向栈上局部数组的指针,这是C语言中典型的未定义行为,具体原因和解决方法如下:

为什么会出现这种现象?

你的tokenizer函数里,char arr[i+1];是在栈帧上分配的局部数组。当函数执行完毕返回时,当前函数的栈帧会被销毁,这块内存会被标记为“可复用”。此时main函数里的p就变成了悬空指针——指向一块已经不属于当前程序有效内存的地址。

至于为什么保留tokenizer里的printf时main能正常打印?这只是巧合:当你在tokenizer内部调用printf时,它没有覆盖arr所在的栈内存区域,返回后这块内存暂时还保留着原来的值。但这种情况完全不可靠,更换编译器、优化等级或者添加其他代码后,结果立刻会变得不可预测。

修复方案

我们需要让返回的指针指向生命周期更长的内存区域,这里提供三种常用的解决思路:

1. 使用静态数组(简单但有局限性)

把局部数组改成静态存储类,这样它的生命周期会持续到程序结束。但要注意静态数组是全局共享的,存在线程不安全、不可重入的问题,且需要预估最大token长度:

char *tokenizer(char s[], const char *delimiter) {
    static char arr[1024]; // 假设token最大长度不超过1023
    int i = 0;
    // 新增判断:避免字符串无分隔符时越界
    while(s[i] != '\0' && s[i] != *delimiter)
        i++;
    int j = 0;
    i = 0;
    while(s[i] != '\0' && s[i] != *delimiter) {
        arr[j] = s[i];
        i++;
        j++;
    }
    arr[j] = '\0';
    return arr;
}

2. 动态分配内存(灵活但需手动释放)

用malloc在堆上分配内存,堆内存不会随函数栈帧销毁,需要调用者在使用完后手动释放,避免内存泄漏:

char *tokenizer(char s[], const char *delimiter) {
    int i = 0;
    while(s[i] != '\0' && s[i] != *delimiter)
        i++;
    // 分配足够存储token和终止符的内存
    char *arr = malloc(i + 1);
    if (arr == NULL) { // 必须检查malloc是否成功
        return NULL;
    }
    int j = 0;
    i = 0;
    while(s[i] != '\0' && s[i] != *delimiter) {
        arr[j] = s[i];
        i++;
        j++;
    }
    arr[j] = '\0';
    return arr;
}

main函数调用时要记得释放内存:

char s[] = "tab-tab";
const char del[2] = "-";
char *p = tokenizer(s, del);
if (p != NULL) {
    printf("%s\n", p);
    free(p); // 释放堆内存
}

3. 让调用者提供缓冲区(最安全)

由调用者传入一个缓冲区和缓冲区大小,函数将结果写入该缓冲区,完全避免内存管理问题:

// 返回值:成功返回缓冲区指针,失败返回NULL(缓冲区不足)
char *tokenizer(char s[], const char *delimiter, char *buf, size_t buf_size) {
    int i = 0;
    while(s[i] != '\0' && s[i] != *delimiter)
        i++;
    // 检查缓冲区是否足够存储token和终止符
    if (i + 1 > buf_size) {
        return NULL;
    }
    int j = 0;
    i = 0;
    while(s[i] != '\0' && s[i] != *delimiter) {
        buf[j] = s[i];
        i++;
        j++;
    }
    buf[j] = '\0';
    return buf;
}

main函数调用示例:

char s[] = "tab-tab";
const char del[2] = "-";
char buf[1024];
char *p = tokenizer(s, del, buf, sizeof(buf));
if (p != NULL) {
    printf("%s\n", p);
}

额外修复:避免越界访问

你原来的代码还有一个隐藏bug:while(s[i] != *delimiter)没有判断s[i]是否为'\0',如果输入字符串中没有分隔符,会一直循环直到越界访问内存,这同样会导致未定义行为。上面的修复方案都已经补充了s[i] != '\0'的判断,一定要加上。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:04:18