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

