为何C语言字符串分割代码在非空格结尾时输出多余字符?
C语言字符串分割出现多余随机字符的问题分析
问题描述
编写了用于分割字符串为单词的C语言代码,运行后输出出现多余的随机字符,例如测试输入"abcd efgh! ijkl mnop"时,输出多了一个P,其他测试用例也有类似问题。代码及输出如下:
代码
void form_rule2(char * str) { int num_words=0; char word[20]; while(sscanf(str,"%s",word)==1) { num_words++; printf("%s\n",word); str+=strlen(word)+1; } } void main() { form_rule2("abcd efgh! ijkl mnop"); }
运行输出
abcd efgh! ijkl mnop P
问题根源
核心问题是指针越界访问未定义内存区域,和NULL终止符直接相关:
- 字符串字面量
"abcd efgh! ijkl mnop"在内存中以\0结尾,当处理完最后一个单词mnop后,str += strlen(word)+1会让指针指向mnop末尾\0的下一个位置,这已经超出了该字符串字面量的内存范围。 - 此时
sscanf会读取越界后的随机内存数据,这些数据可能包含可打印字符,就会出现多余的输出,属于C语言中的未定义行为。 - 另外,
str += strlen(word)+1的逻辑本身存在漏洞:如果单词之间有多个空白字符(比如多个空格),该代码只会跳过一个字符,后续sscanf会自动忽略空白,但最终越界的问题依然存在。
修正方案
方案1:安全遍历指针,避免越界
使用临时指针遍历字符串,跳过空白字符,确保指针不会超出字符串范围:
#include <stdio.h> #include <string.h> #include <ctype.h> void form_rule2(const char *str) { int num_words = 0; char word[20]; const char *ptr = str; while (sscanf(ptr, "%s", word) == 1) { num_words++; printf("%s\n", word); // 移动指针到当前单词末尾 ptr += strlen(word); // 跳过所有连续空白字符 while (*ptr != '\0' && isspace((unsigned char)*ptr)) { ptr++; } // 到达字符串末尾则终止循环 if (*ptr == '\0') { break; } } } int main() { form_rule2("abcd efgh! ijkl mnop"); return 0; }
方案2:使用标准库函数strtok
strtok是C标准库中专门用于字符串分割的函数,更简洁可靠:
#include <stdio.h> #include <string.h> void form_rule2(char *str) { int num_words = 0; // 按空白字符(空格、制表符、换行符)分割字符串 char *word = strtok(str, " \t\n"); while (word != NULL) { num_words++; printf("%s\n", word); word = strtok(NULL, " \t\n"); } } int main() { // 字符串字面量不可修改,需复制到可写栈数组中 char str[] = "abcd efgh! ijkl mnop"; form_rule2(str); return 0; }
额外注意事项
- 字符串字面量属于只读内存,不能直接修改,也不要访问其末尾
\0之外的内存。 main函数的标准返回类型是int,而非void,gcc编译器会对void main()发出警告,建议改为标准写法。
内容的提问来源于stack exchange,提问作者Kuroro
相关产品推荐
相关产品推荐

