【嵌入式 C 语言实战】字符串单词统计功能详解(附完整代码解析)
大家好,我是学嵌入式的小杨同学。在嵌入式开发中,字符串处理是高频需求(如串口数据解析、配置项解析等),今天带大家解析一份完整的字符串单词个数统计代码,理解其核心逻辑、实现细节以及嵌入式场景下的优化建议。
一、代码整体功能概述
这份代码实现了一个核心功能:接收用户输入的字符串,统计其中的单词个数(以空格作为单词分隔符),具备输入处理、字符串截断、单词计数三大核心模块,代码简洁高效,适合入门字符串处理学习。
二、逐模块详细解析
1. 头文件引入说明
c
运行
#include <stdio.h>
#include <string.h>
stdio.h:提供标准输入输出函数支持,如printf(打印)、fgets(读取字符串);string.h:提供字符串处理函数支持,如strcspn(用于截断换行符),这是 C 语言字符串处理的必备头文件。
2. 核心函数:count_words 单词统计函数
这是整个程序的核心,负责实现单词个数的统计逻辑,采用状态标记法实现,效率为 O (n)(n 为字符串长度,仅需遍历一次字符串)。
c
运行
int count_words(const char *str) {
int count = 0; // 单词计数器,记录最终单词个数
int in_word = 0; // 状态标记:0表示当前处于单词外(空格/字符串开头),1表示当前处于单词内
// 遍历字符串,直到遇到字符串结束符'\0'
while (*str != '\0') {
// 情况1:当前字符是空格,标记为"单词外"状态
if (*str == ' ') {
in_word = 0;
}
// 情况2:当前字符不是空格,且处于"单词外"状态(说明遇到了新单词的开头)
else if (in_word == 0)
{
count++; // 单词计数+1
in_word = 1; // 标记为"单词内"状态,避免同一单词重复计数
}
str++; // 指针后移,遍历下一个字符
}
return count;
}
关键细节解析
- 函数参数
const char *str:使用const修饰字符串指针,表明函数内部不会修改字符串内容,是 C 语言的良好编码习惯,提升代码安全性和可读性; - 状态标记
in_word:核心设计亮点,避免了对每个单词的重复遍历,仅通过 0/1 状态切换,就能准确识别新单词的开头,高效且简洁; - 遍历逻辑
while (*str != '\0'):通过指针遍历字符串,无需知道字符串长度,符合 C 语言字符串以'\0'结尾的特性; - 计数规则:仅当 “遇到非空格字符” 且 “当前处于单词外” 时,才进行计数,确保了多个连续空格不会被误判为多个单词分隔符。
注意点
该函数当前仅支持单个空格作为单词分隔符,若输入多个连续空格(如"hello world"),仍能正确统计为 2 个单词,但不支持制表符\t、换行符\n等其他分隔符(后续可优化)。
3. 主函数:main 输入处理与功能调用
主函数负责搭建用户交互流程,完成字符串输入、预处理、单词统计和结果输出。
c
运行
int main() {
char str[1024]; // 定义字符数组,用于存储用户输入的字符串,容量1024足够日常使用
printf("请输入字符串:");
// 步骤1:读取用户输入的字符串
fgets(str, sizeof(str), stdin);
// 步骤2:截断字符串末尾的换行符(关键处理)
str[strcspn(str, "\n")] = '\0';
// 步骤3:调用单词统计函数,获取结果
int res = count_words(str);
// 步骤4:输出统计结果
printf("单词个数:%d\n", res);
return 0;
}
核心步骤详解
-
字符串读取:
fgets函数的使用- 选用
fgets而非scanf:fgets支持读取包含空格的字符串,而scanf遇到空格会停止读取,无法满足 “统计多单词” 的需求; - 参数说明:
str(存储输入的字符数组)、sizeof(str)(读取的最大长度,避免数组越界)、stdin(从标准输入(终端)读取数据); - 安全特性:
fgets会自动在字符串末尾添加'\0',无需手动添加,有效避免数组越界风险。
- 选用
-
换行符截断:
strcspn函数的核心作用- 问题背景:
fgets读取用户输入时,会将用户按下的Enter键(换行符\n)也读取到字符串中,例如输入"hello world",str中实际存储的是"hello world\n",换行符会被count_words函数判定为 “非空格字符”,可能影响统计结果; strcspn(str, "\n")功能:计算str中首次出现'\n'的位置索引(返回值为换行符在字符串中的下标);- 处理逻辑:
str[strcspn(str, "\n")] = '\0':将换行符所在位置替换为字符串结束符'\0',实现 “截断换行符” 的效果,确保字符串格式正确。
- 问题背景:
-
结果输出:调用
count_words函数,打印统计结果,完成整个流程。
三、编译运行与测试案例
1. 编译命令(Linux/macOS)
将代码保存为word_count.c,在终端执行以下命令:
bash
运行
gcc word_count.c -o word_count
./word_count
2. 测试案例与预期结果
案例 1:正常输入(单个空格分隔)
plaintext
请输入字符串:hello world
单词个数:2
案例 2:多个连续空格分隔
plaintext
请输入字符串:I love embedded C
单词个数:4
案例 3:首尾带空格
plaintext
请输入字符串: this is a test
单词个数:4
案例 4:空字符串(直接按 Enter)
plaintext
请输入字符串:
单词个数:0
四、嵌入式开发优化建议(贴合实际应用场景)
-
优化字符数组容量,避免内存浪费
- 嵌入式设备内存资源受限,无需定义 1024 长度的字符数组,可根据实际需求调整(如
char str[64]),满足需求的同时减少内存占用; - 若需要处理不定长字符串,可采用 “静态内存池” 替代固定数组,避免动态分配
malloc导致的内存碎片化。
- 嵌入式设备内存资源受限,无需定义 1024 长度的字符数组,可根据实际需求调整(如
-
支持多种分隔符,提升实用性
- 实际嵌入式场景中,字符串分隔符可能包含
\t(制表符)、\n(换行符)、,(逗号)等,可修改count_words函数的判断逻辑,支持多分隔符:
c
运行
// 优化:判断是否为分隔符(空格、制表符) if (*str == ' ' || *str == '\t') { in_word = 0; } - 实际嵌入式场景中,字符串分隔符可能包含
-
添加输入长度校验,提升健壮性
fgets最多读取sizeof(str)-1个字符(预留'\0'的位置),若用户输入超出数组容量,会截断字符串,可添加校验逻辑,提示用户输入过长:
c
运行
// 检查是否读取了完整的输入(是否包含换行符) if (str[strcspn(str, "\n")] != '\n' && strlen(str) == sizeof(str)-1) { printf("警告:输入字符串过长,已截断!\n"); } -
避免栈溢出,采用全局 / 静态存储
- 嵌入式设备的栈空间较小(通常几 KB),
main函数中的局部字符数组str存储在栈上,若长度过大可能导致栈溢出,可改为全局数组或静态数组(存储在全局 / 静态存储区):
c
运行
// 全局数组,避免栈溢出 char str[1024]; int main() { // ... 后续逻辑不变 } - 嵌入式设备的栈空间较小(通常几 KB),
-
添加内存释放逻辑(若使用动态分配)
- 若后续优化为动态分配字符串内存,使用完毕后必须调用
free释放内存,避免内存泄漏,确保嵌入式系统长期稳定运行。
- 若后续优化为动态分配字符串内存,使用完毕后必须调用
五、总结
- 这份单词统计代码的核心是 **
in_word状态标记法 **,仅遍历一次字符串即可完成统计,效率高效,是字符串处理的经典设计; fgets+strcspn的组合是安全读取带空格字符串的最佳实践,有效避免了换行符和数组越界问题;- 嵌入式开发中使用字符串处理功能,需重点关注内存占用、健壮性、栈溢出三大问题,兼顾功能实现和系统稳定性;
- 该代码可作为嵌入式字符串解析的基础模板,后续可扩展为配置项解析、串口数据解析等实用功能。
我是学嵌入式的小杨同学,关注我,解锁更多嵌入式 C 语言字符串处理的进阶技巧!