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

C语言str_split函数仅返回首个单词问题求助

字符串分割函数str_split输出异常问题排查

问题描述

我编写了一个接收字符串并返回单词字符串数组的str_split函数,代码如下:

#include "main.h"

/**
 * str_split - Splits a string
 * @str: The string that will be splited
 * 
 * Return: On success, it returns the new array
 * of strings. On failure, it returns NULL
 */
char **str_split(char *str)
{   
    char *piece, **str_arr = NULL, *str_cpy;
    int number_of_words = 0, i;

    if (str == NULL)
    {
        return (NULL);
    }
    str_cpy = str;
    piece = strtok(str_cpy, " ");
    while (piece != NULL)
    {
        if ((*piece) == '\n')
        {
            piece = strtok(NULL, " ");
            continue;
        }
        number_of_words++;
        piece = strtok(NULL, " ");
    }
    
    str_arr = (char **)malloc(sizeof(char *) * number_of_words);
    piece = strtok(str, " ");
    for (i = 0; piece != NULL; i++)
    {
        if ((*piece) == '\n')
        {
            piece = strtok(NULL, " ");
            continue;
        }
        str_arr[i] = (char *)malloc(sizeof(char) * (strlen(piece) + 1));
        strcpy(str_arr[i], piece);
        piece = strtok(NULL, " ");
    }
    return (str_arr);
}

编译运行后,预期输出:

Hello
World

但实际仅输出:

Hello

我已通过遍历原字符串副本统计单词数来动态分配数组内存,请问是否因数组空间不足导致该问题?

问题分析与解决方案

你的问题不是数组空间不足,核心错误出在字符串副本的创建和strtok的使用逻辑上:

1. 错误的字符串副本创建

你写的str_cpy = str;只是让指针str_cpy指向原字符串str的内存地址,并没有真正复制字符串内容。第一次调用strtok(str_cpy, " ")时,strtok会直接修改原字符串的内存(将分隔空格替换为\0),导致原字符串被破坏。第二次调用strtok(str, " ")时,原字符串已经不是完整的初始字符串,只能读取到第一个被分割出来的单词。

修复方式:创建真正的字符串副本,使用strdup或手动malloc+strcpy:

// 使用strdup(需包含<string.h>)
str_cpy = strdup(str);
if (!str_cpy) {
    return NULL;
}

// 或者手动分配内存并复制
str_cpy = malloc(sizeof(char) * (strlen(str) + 1));
if (!str_cpy) {
    return NULL;
}
strcpy(str_cpy, str);

2. 冗余的换行符判断逻辑

你单独判断(*piece) == '\n'来跳过换行,但这种方式只能处理第一个字符是换行的情况,无法处理包含换行的单词或其他位置的换行。更合理的做法是将换行符加入strtok的分隔符集合,让strtok自动跳过空格和换行:

// 第一次遍历统计单词数
piece = strtok(str_cpy, " \n");
// 第二次分割原字符串
piece = strtok(str, " \n");

3. 未添加数组终止符

当前返回的字符串数组没有以NULL结尾,调用者无法通过遍历判断数组的结束位置。需要在分配数组内存时多留一个char*的空间,最后设置终止符:

// 多分配一个位置用于存放NULL
str_arr = malloc(sizeof(char *) * (number_of_words + 1));
// ... 分割赋值完成后 ...
str_arr[number_of_words] = NULL;

4. 内存泄漏防护

如果在分配单个单词内存时malloc失败,需要释放之前已经分配的内存,避免泄漏:

str_arr[i] = malloc(sizeof(char) * (strlen(piece) + 1));
if (!str_arr[i]) {
    // 释放已分配的单词内存
    for (int j = 0; j < i; j++) {
        free(str_arr[j]);
    }
    free(str_arr);
    // 释放之前创建的字符串副本
    free(str_cpy);
    return NULL;
}

修复后的完整代码示例

#include "main.h"
#include <string.h>
#include <stdlib.h>

/**
 * str_split - Splits a string into words (split by spaces and newlines)
 * @str: The string to be split
 * 
 * Return: On success, returns a NULL-terminated array of strings.
 * On failure, returns NULL.
 */
char **str_split(char *str)
{   
    char *piece, **str_arr = NULL, *str_cpy;
    int number_of_words = 0, i;

    if (str == NULL)
    {
        return NULL;
    }

    // 创建原字符串的副本,避免修改原字符串
    str_cpy = strdup(str);
    if (!str_cpy) {
        return NULL;
    }

    // 第一次遍历统计单词数量
    piece = strtok(str_cpy, " \n");
    while (piece != NULL)
    {
        number_of_words++;
        piece = strtok(NULL, " \n");
    }
    // 统计完成后释放副本
    free(str_cpy);

    // 分配字符串数组内存(包含NULL终止符)
    str_arr = malloc(sizeof(char *) * (number_of_words + 1));
    if (!str_arr) {
        return NULL;
    }

    // 第二次遍历分割原字符串并赋值
    piece = strtok(str, " \n");
    for (i = 0; piece != NULL; i++)
    {
        str_arr[i] = malloc(sizeof(char) * (strlen(piece) + 1));
        if (!str_arr[i]) {
            // 内存分配失败,回滚已分配的内存
            for (int j = 0; j < i; j++) {
                free(str_arr[j]);
            }
            free(str_arr);
            return NULL;
        }
        strcpy(str_arr[i], piece);
        piece = strtok(NULL, " \n");
    }
    // 添加数组终止符
    str_arr[number_of_words] = NULL;

    return str_arr;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 08:55:19