在MCU中并不支持sscanf_s,所以我们只能通过sscanf实现。
charuser[33]={0};charpass[33]={0};sscanf_s(buf,"%s %s",user,pass);// 用 sscanf实现sscanf(buf,"%32[^ ] %32[^ ]",user,pass);C 库函数 int sscanf(const char *str, const char *format, …) 从字符串读取格式化输入。
声明
下面是 sscanf() 函数的声明。
int sscanf(const char *str, const charformat, …)
参数
str – 这是 C 字符串,是函数检索数据的源。
format – 这是 C 字符串,包含了以下各项中的一个或多个:空格字符、非空格字符 和 format 说明符。
format 说明符形式为 [=%[][width][modifiers]type=],具体讲解如下:
参数 描述
- 这是一个可选的星号,表示数据是从流 stream 中读取的,但是可以被忽视,即它不存储在对应的参数中。
width 这指定了在当前读取操作中读取的最大字符数。
modifiers 为对应的附加参数所指向的数据指定一个不同于整型(针对 d、i 和 n)、无符号整型(针对 o、u 和 x)或浮点型(针对 e、f 和 g)的大小: h :短整型(针对 d、i 和 n),或无符号短整型(针对 o、u 和 x) l :长整型(针对 d、i 和 n),或无符号长整型(针对 o、u 和 x),或双精度型(针对 e、f 和 g) L :长双精度型(针对 e、f 和 g)
type 一个字符,指定了要被读取的数据类型以及数据读取方式。具体参见下一个表格。
前言
在嵌入式 MCU 开发中,标准 C 库函数的使用往往受到平台限制。例如,许多 MCU 的编译器并不支持sscanf_s这类带安全后缀的函数,开发者只能退而使用传统的sscanf。本文将从sscanf的声明、参数和格式化说明符入手,结合 MCU 场景下的实际代码示例,帮助读者正确、安全地在嵌入式环境中解析字符串。
1. sscanf 函数基础
1.1 函数声明与作用
sscanf是 C 标准库提供的格式化输入函数,其声明如下:
intsscanf(constchar*str,constchar*format,...);它从字符串str中读取数据,并根据format指定的格式进行解析,将结果存入后续可变参数指向的变量中。
1.2 参数详解
str:数据源字符串,即待解析的输入。format:格式控制字符串,可包含空格字符、非空格字符以及格式说明符。- 可变参数:用于接收解析结果的变量地址。
1.3 格式说明符组成
格式说明符的通用形式为%[*][width][modifiers]type,各字段含义如下:
| 字段 | 说明 |
|---|---|
* | 可选,表示读取但忽略该数据,不存入参数 |
width | 指定本次读取的最大字符数 |
modifiers | 指定数据类型大小,如h(短整型)、l(长整型/双精度)、L(长双精度) |
type | 指定读取的数据类型及方式 |
2. MCU 环境下的 sscanf 实践
2.1 为什么 MCU 不支持 sscanf_s
sscanf_s是微软在 Visual C++ 中提供的安全增强版本,要求显式传入缓冲区大小,以防范缓冲区溢出。而大多数嵌入式编译器(如 GCC ARM、IAR、Keil)仅提供标准 C 库的sscanf,因此跨平台移植时需使用标准版本。
下表从参数、安全性、平台支持和使用示例等方面对比sscanf与sscanf_s的差异:
| 对比项 | sscanf | sscanf_s |
|---|---|---|
| 参数 | 可变参数直接传入目标缓冲区地址,不携带缓冲区大小 | 每个缓冲区参数前需额外传入缓冲区大小(如sizeof(user)) |
| 安全性 | 不检查缓冲区边界,%s等格式符可能造成缓冲区溢出 | 运行时校验缓冲区大小,越界写入会被拒绝,安全性更高 |
| 平台支持 | C 标准库函数,GCC ARM、IAR、Keil 等嵌入式编译器普遍支持 | 微软 Visual C++ 扩展,多数 MCU 编译器不提供 |
| 使用示例 | sscanf(buf, "%32[^ ] %32[^ ]", user, pass); | sscanf_s(buf, "%s %s", user, sizeof(user), pass, sizeof(pass)); |
| 可移植性 | 跨平台、跨编译器通用 | 仅限 Windows/Visual C++ 环境,移植性差 |
在 MCU 开发中,由于目标编译器通常只提供标准 C 库,应优先选择sscanf,并通过width限制(如%32[^ ])和返回值检查来弥补其安全性不足;只有在明确运行于支持sscanf_s的桌面环境时,才考虑使用sscanf_s以获得更强的缓冲区保护。
2.2 使用 sscanf 解析用户名与密码
在 MCU 中解析类似"username password"的字符串时,可使用如下代码:
charuser[33]={0};charpass[33]={0};// 使用 sscanf 实现,限制最大读取长度,防止缓冲区溢出intret=sscanf(buf,"%32[^ ] %32[^ ]",user,pass);// 检查返回值:成功匹配并赋值的参数个数应为 2if(ret!=2){// 解析失败,返回错误码或设置默认值strcpy(user,"default_user");strcpy(pass,"default_pass");// 或者:return -1; // 由调用方决定如何处理}通过%32[^ ]限制最多读取 32 个字符,并在遇到空格时停止,从而保证写入user和pass的数据不会越界。
sscanf的返回值表示成功匹配并赋值的参数个数。当输入格式不符合预期(例如缺少空格、字段缺失)时,返回值会小于 2,此时应主动处理解析失败的情况:
- 返回错误码:在函数中直接
return -1,由上层调用方根据错误码决定后续流程,适合需要严格校验输入的场景。 - 设置默认值:为
user和pass填充默认值,保证程序继续运行,适合对输入容忍度较高的场景。
无论采用哪种策略,都应在解析后先检查返回值,避免使用未初始化的缓冲区内容。
2.3 常见陷阱与注意事项
- 未指定
width时,%s可能造成缓冲区溢出,务必配合最大长度使用。 - 解析失败时
sscanf返回成功匹配并赋值的参数个数,应检查返回值以判断解析是否完整。 - 字符串中包含空格时,应使用
%[^...]集合格式而非%s。
3. 总结
sscanf是 MCU 开发中解析字符串的常用工具,虽然缺少sscanf_s的安全检查,但通过合理使用width限制和%[^...]格式符,同样可以写出安全可靠的解析代码。理解其声明、参数和格式说明符,是正确使用的前提。
参考资料
- C 标准库参考文档:
sscanf函数说明 - 嵌入式 C 编程实践:字符串解析与缓冲区安全