Arduino开发中初始化定长char数组后调用strlen获取长度异常
问题核心成因
所有异常输出的根源是对C风格字符串的终止规则、以及strlen/串口打印函数的工作逻辑存在认知偏差,两类问题叠加导致了越界访问内存的行为:
- C字符串的强制规则:合法的C风格字符串,必须以ASCII值为0的空字符
'\0'作为结束标记,没有这个标记的char数组不属于可被字符串接口安全处理的对象。 strlen()的真实工作逻辑:它不会根据你定义的数组固定长度计算长度,而是从传入的内存地址开始逐字节向后扫描,直到遇到第一个'\0'才停止,返回扫描过的总字节数。如果传入的数组没有内置终止符,函数会越界读取数组边界外的随机内存内容,直到碰巧扫到一个0值字节才终止。你看到的20、14、9、5、2这类长度值,全是越界读取得到的随机结果,和数组本身的实际长度毫无关系。- 代码本身的隐患:你定义的所有char数组,都被有效非零字符完全填满,没有预留任何位置存放
'\0'终止符。比如长度为6的testArray6被'1'~'6'全部占满,Serial.println(testArray6)打印时找不到终止符,就顺着内存地址向后读取相邻内存的内容——刚好它后面相邻的内存位置,就是你后续依次定义的几个全'0'的字节数组,最后就拼出了一长串12345600000000000000的异常打印结果。
对应解决方案
按照字符串的使用场景调整写法即可:
- 当需要用char数组存储可被字符串接口处理的文本时,必须比计划存储的有效字符数多预留1个字节的空间,专门用来存放终止符
'\0'。 - 两类安全的初始化写法:
- 显式补全终止符:如果要存6个有效字符,就定义长度为7的数组,在最后一个元素位置写入
'\0',例如char testArray6[7] = {'1', '2', '3', '4', '5', '6', '\0'}; - 用字符串字面量初始化:编译器会自动在末尾补上
'\0',例如char testArray6[] = "123456";,这种写法编译器会自动计算数组长度为7,无需手动数长度。
- 显式补全终止符:如果要存6个有效字符,就定义长度为7的数组,在最后一个元素位置写入
- 如果是用char数组存储纯二进制数据(不需要当文本处理),绝对不要把这类数组传给
strlen()或者Serial.println(字符数组)这类依赖终止符的接口,这类场景下要按照预先知道的固定数组长度做读写操作。 - 修正后的可正常运行的示例代码:
Serial.begin(57600); char testArray6[7] = {'1', '2', '3', '4', '5', '6', '\0'}; Serial.println(testArray6); // 输出123456 Serial.println(strlen(testArray6)); // 输出正确长度6 char testArray5[6] = {'0', '0', '0', '0', '0', '\0'}; Serial.println(strlen(testArray5)); // 输出正确长度5 char testArray4[5] = {'0', '0', '0', '0', '\0'}; Serial.println(strlen(testArray4)); // 输出正确长度4 char testArray3[4] = {'0', '0', '0', '\0'}; Serial.println(strlen(testArray3)); // 输出正确长度3 char testArray2[3] = {'0', '0', '\0'}; Serial.println(strlen(testArray2)); // 输出正确长度2
内容的提问来源于stack exchange,提问作者Conner Vinyard
相关产品推荐
相关产品推荐

