# C 语言字符串库函数参考 — STM32 嵌入式开发指南

发布时间:2026/6/27 18:50:47
# C 语言字符串库函数参考 — STM32 嵌入式开发指南 聚焦 STM32 嵌入式开发场景整理string.h及stdio.h中常用字符串操作函数的分类、原型、使用场景与安全注意事项。适用平台STM32 全系列标准库Newlib / Newlib-nano核心原则安全 · 轻量 · 可重入规范标准C99 / POSIX一、长度查询类获取字符串长度是 STM32 数据处理最频繁的操作注意空指针与缓冲区边界。1.1 strlensize_tstrlen(constchar*s);返回字符串长度不含\0。⚠️STM32 注意空指针、无限读取风险。如果s指向的内存没有\0会导致越界读取。示例charbuf[]Hello STM32;size_tlenstrlen(buf);// len 111.2 strnlensize_tstrnlen(constchar*s,size_tmaxlen);限制最大长度的strlen最多读取maxlen个字符。✅STM32 推荐首选函数防止无限读取导致 HardFault。示例charbuf[64];// 从 UART 接收数据后安全获取长度size_tlenstrnlen(buf,sizeof(buf)-1);二、比较类字符串比较广泛应用于指令解析如 AT 命令、协议帧校验等场景。2.1 strcmpintstrcmp(constchar*s1,constchar*s2);逐字符比较区分大小写返回 0 表示相等。⚠️STM32 注意无边界检查注意输入长度。两个字符串都必须以\0结尾。示例if(strcmp(cmd,RESET)0){system_reset();}2.2 strncmpintstrncmp(constchar*s1,constchar*s2,size_tn);仅比较前n个字符。✅STM32 推荐首选函数防溢出。比较 AT 命令前缀时尤其有用。示例// 安全比较 AT 命令前缀if(strncmp(cmd,AT,3)0){process_at_command(cmd3);}// 比较响应结果固定长度if(strncmp(rx_buf,OK\r\n,4)0){// 命令执行成功}2.3 strcasecmpintstrcasecmp(constchar*s1,constchar*s2);不区分大小写比较POSIX 扩展Newlib 支持。⚠️STM32 注意部分库支持Newlib 有注意重入问题。非标准 C99 函数。示例if(strcasecmp(method,GET)0){handle_get_request();}三、复制 / 填充类内存操作函数在 STM32 中极其高频用于数据缓冲、协议帧构造、外设 DMA 传输等。3.1 strcpychar*strcpy(char*dest,constchar*src);复制字符串含\0。⛔绝对禁止无边界检查在 STM32 中极易导致缓冲区溢出引发 HardFault 或内存越界。反面教材不要用// ❌ 危险dest 溢出无感知chardest[8];strcpy(dest,Hello World!);// 溢出3.2 strncpychar*strncpy(char*dest,constchar*src,size_tn);复制最多n个字符到dest。⚠️谨慎使用不保证\0终止。如果src长度 ndest将不会以\0结尾。示例charbuf[32];strncpy(buf,src,sizeof(buf)-1);buf[sizeof(buf)-1]\0;// 必须手动补终止符3.3 memcpyvoid*memcpy(void*dest,constvoid*src,size_tn);内存块复制不关心\0效率最高。✅STM32 推荐高频使用效率最高。适用于已知长度的二进制数据复制。示例// 复制已知长度的数据uint8_trx_data[16];memcpy(rx_data,dma_buffer,16);// 复制字符串已知长度时比 strncpy 更安全memcpy(buf,src,len);if(lensizeof(buf))buf[len]\0;3.4 memsetvoid*memset(void*s,intc,size_tn);内存块填充常用于清空缓冲区。✅STM32 推荐DMA 清空、结构体初始化必备。示例// 清空结构体SensorData_t sensor;memset(sensor,0,sizeof(sensor));// 清空 UART 接收缓冲区memset(rx_buf,0,sizeof(rx_buf));四、连接 / 拼接类字符串拼接在构建协议报文如 MQTT、HTTP、Modbus中非常常见。4.1 strcatchar*strcat(char*dest,constchar*src);拼接字符串。⛔绝对禁止极易溢出。dest必须有足够的剩余空间但无法检查。反面教材不要用// ❌ 危险charbuf[16]Temp:;strcat(buf,123.456789);// 可能溢出4.2 strncatchar*strncat(char*dest,constchar*src,size_tn);最多拼接n个字符。⚠️谨慎使用需确保dest有剩余空间且本身已\0终止。示例charbuf[32]Sensor;strncat(buf,_Data,sizeof(buf)-strlen(buf)-1);4.3 snprintfintsnprintf(char*s,size_tn,constchar*fmt,...);格式化拼接强烈推荐。✅STM32 必备C99 标准安全灵活。替代所有strcat/strcpy/sprintf。示例// 构建传感器数据上报报文JSONcharmsg[64];intlensnprintf(msg,sizeof(msg),{\temp\:%.1f,\hum\:%.1f,\id\:\%s\},temperature,humidity,device_id);// 构建 MQTT Topicchartopic[48];snprintf(topic,sizeof(topic),sensor/%s/data,device_id);// 构建 AT 命令charat_cmd[32];snprintf(at_cmd,sizeof(at_cmd),ATSEND%d,%s\r\n,data_len,payload);五、查找 / 搜索类解析协议帧、提取关键字段时经常使用查找函数。5.1 strchrchar*strchr(constchar*s,intc);查找字符首次出现。示例char*colonstrchr(http_header,:);if(colon){*colon\0;// 分隔键和值char*valuecolon1;}5.2 strrchrchar*strrchr(constchar*s,intc);查找字符最后一次出现。示例// 取文件扩展名最后一次出现的 .char*extstrrchr(filename,.);if(extstrcmp(ext,.bin)0){// 扩展名为 .bin执行固件升级}5.3 strstrchar*strstr(constchar*haystack,constchar*needle);查找子串。示例// 定位帧结束符 \r\nchar*pstrstr(rx_buf,\r\n);if(p){size_tframe_lenp-rx_buf;// 处理完整帧...}// 检查响应中是否包含 ERRORif(strstr(response,ERROR)!NULL){handle_error();}5.4 strpbrkchar*strpbrk(constchar*s,constchar*accept);查找第一个出现在accept集合中的字符。示例// 查找第一个逗号或分号分隔符char*delimstrpbrk(line,,;);if(delim)*delim\0;// 截断字符串5.5 memchrvoid*memchr(constvoid*s,intc,size_tn);内存中查找字符有限长度。示例// 在二进制数据中查找分隔符uint8_t*pmemchr(rx_buffer,0xAA,rx_len);if(p){size_toffsetp-rx_buffer;}// 安全获取字符串长度二进制文本混合数据size_tblen(char*)memchr(buf,\0,sizeof(buf))-buf;六、转换类数值与字符串之间的转换是嵌入式开发中最容易出错的环节之一。6.1 atoiintatoi(constchar*nptr);字符串转int。⛔不推荐无错误处理。非法输入时行为未定义返回 0 无法区分输入为0和转换失败。反面教材不要用// ❌ 无法判断 abc 和 0intvalatoi(abc);// val 0但不知道是错误6.2 atollongatol(constchar*nptr);字符串转long。⛔不推荐同atoi无错误处理。6.3 strtollongstrtol(constchar*nptr,char**endptr,intbase);字符串转long支持进制转换 错误检测强烈推荐。✅STM32 推荐通过endptr精确判断转换位置检测非法字符支持 2/8/10/16 进制。示例// 带错误检查的字符串转整数char*end;longvalstrtol(str,end,10);if(*end!\0){// 转换不完整str 中包含非数字字符printf(Invalid character: %c\n,*end);}// 16 进制字符串转整数如解析地址uint32_taddrstrtol(0x08000000,NULL,16);// addr 0x08000000// 2 进制字符串转整数intflagsstrtol(1010,NULL,2);// flags 106.4 sprintfintsprintf(char*s,constchar*fmt,...);格式化输出到字符串。⛔危险无边界检查缓冲区溢出风险。嵌入式中禁止使用。反面教材不要用// ❌ 危险charbuf[8];intx12345;sprintf(buf,%d,x);// 写入 123455字符但如果 x 很大就溢出6.5 snprintfintsnprintf(char*s,size_tn,constchar*fmt,...);安全版sprintf必备。✅STM32 必备始终限制写入长度返回值可判断是否截断。示例// 浮点数格式化STM32 传感器常用charvolt_str[16];snprintf(volt_str,sizeof(volt_str),%.2fV,3.14159);// 3.14V// 带截断检测的格式化charbuf[10];intnsnprintf(buf,sizeof(buf),Temperature: %d,250);if(nsizeof(buf)){// 输出被截断了}七、实用工具类这些函数在日常嵌入式开发中虽然不是最常用但特定场景下非常高效。7.1 strtokchar*strtok(char*str,constchar*delim);按分隔符切分字符串非线程安全。⚠️STM32 注意会修改原字符串将分隔符替换为\0且非线程安全。FreeRTOS 下请使用strtok_r。示例// 解析 CSV 数据charline[]12.5,68.3,1013.2;char*tokenstrtok(line,,);while(token){floatvalatof(token);process(val);tokenstrtok(NULL,,);}7.2 strtok_rchar*strtok_r(char*str,constchar*delim,char**saveptr);线程安全的strtokGNU 扩展 / POSIX。✅STM32 推荐FreeRTOS 环境下使用。通过saveptr保存状态可重入。示例// FreeRTOS 下安全解析charline[]cmd,arg1,arg2;char*saveptr;char*cmdstrtok_r(line,,,saveptr);char*arg1strtok_r(NULL,,,saveptr);char*arg2strtok_r(NULL,,,saveptr);7.3 strspnsize_tstrspn(constchar*s,constchar*accept);返回s开头accept字符集的连续长度。示例// 跳过前导空白字符constchar*s \t\nHello;size_toffsetstrspn(s, \t\n);constchar*contentsoffset;// 指向 Hello// 验证字符串是否全为数字if(strspn(input,0123456789)strlen(input)){// 是纯数字字符串}7.4 strcspnsize_tstrcspn(constchar*s,constchar*reject);返回s开头直到出现reject字符集的长度。示例// 查找第一个非数字字符的位置constchar*num_str12345abc;size_tnum_lenstrcspn(num_str,abcdefghijklmnopqrstuvwxyz);// num_len 5// 获取一行文本直到换行符size_tline_lenstrcspn(buffer,\r\n);八、陷阱与实战技巧STM32 资源受限字符串处理不当极易引发难以调试的 Crash。类别问题解决方案⛔ 危险strcpy/strcat/sprintf无边界检查全部替换为strncpy/snprintf⚠️ 注意strncpy不保证\0终止手动buf[sizeof(buf)-1] \0⛔ 危险strtok修改原字符串且非线程安全FreeRTOS 用strtok_r或自实现⚠️ 注意Flash 字符串字面量不可修改不要对字符串字面量调用strtok/strcpy目标不能是字面量 性能Newlib 标准库体积大使用Newlib-nano或链接--specsnano.specs安全宏定义模板/* stm32_string_utils.h */// 安全的字符串拷贝宏dst 必须是数组不能是指针#defineSAFE_STRCPY(dst,src)do{\strncpy((dst),(src),sizeof(dst)-1);\(dst)[sizeof(dst)-1]\0;\}while(0)// 安全的字符串格式化宏dst 必须是数组不能是指针#defineSAFE_SPRINTF(dst,fmt,...)\snprintf((dst),sizeof(dst),fmt,##__VA_ARGS__)// 线程安全的 strtok 封装FreeRTOSstaticinlinechar*safe_strtok(char*str,constchar*delim,char**saveptr){returnstrtok_r(str,delim,saveptr);}九、速查表推荐函数日常使用首选函数用途替代strnlen安全获取长度strlenstrncmp安全比较strcmpmemcpy内存块复制strcpymemset清空/初始化手动循环snprintf安全格式化sprintf/strcatstrstr子串搜索手动查找strchr字符查找手动查找strtok_r线程安全切分strtokstrtol带错误检查的转换atoi/atolstrpbrk多分隔符查找多次strchrmemchr二进制数据查找手动查找memmove重叠内存复制memcpy有重叠时绝对禁止strcpy · strcat · sprintf谨慎使用strncpy需手动补 \0 · strtok非 RTOS · atoi无错误处理总结STM32 下字符串处理的核心原则是 ——优先strn*族、摒弃str*无边界函数、强制用snprintf、配合 RTOS 时注意重入性。