arduino简单string入门——如何控制字符串内存使用量
在方法中使用字符串
如上文指南所述,长寿命字符串应与reserve()一起使用以防止碎片化。通常这些长寿命字符串是全局的。如果loop()方法中有任何字符串,那么它们在所有其他代码运行时都存在。因此它们也是“长寿命的”,因此将它们移出以成为全局变量并在setup() 中为它们保留空间。
有时,方法中的字符串会相对较长。例如,重复用于解析 CSV(逗号分隔值)或 GPS 数据的字段字符串。在这种情况下,您应该在解析方法的顶部保留足够的空间以防止本地碎片。解析方法返回时,所有碎片都将被清除,但与此同时,它正在使用可能供不应求的额外内存。另请参阅 下面的最小化字符串内存使用量。例如
bool parse(String& lineOfData) { String field; StringReserveCheck fieldCheck; field.reserve(32); // 最长预期字段 fieldCheck.init(field,Serial); // 当解析退出时,StringReserveCheck 析构函数将执行 checkReserve() 并将消息打印到 Serial . . . }
临时字符串
下面的最小化字符串内存向您展示了如何避免在大多数情况下创建短暂的临时字符串。但有时它们是不可避免的。示例包括添加浮点数或添加十六进制格式的整数以及使用创建并返回字符串的子字符串函数。例如在以下语句中
int i = 32; str = input.substring(15); str += F(" 0x"); str += String(i,2); // i 为十六进制格式
现代编译器似乎擅长优化那些不再需要的临时字符串,并且无论如何它们都会在方法结束时被完全处理掉。如果你有一个很长很复杂的方法,并且想“强制”清理这些临时字符串,那么你可以在那段代码周围使用 { },例如
int i = 32; { str = input.substring(15); str += F(" 0x"); str += String(i,16); // i 为十六进制格式 } // 任何临时字符串都会在这里被编译器清理。
最小化字符串内存使用量
正如我们上面所看到的,您可以使用reserve( )和 StringReserveCheck来防止碎片。然而,在文本处理方法中创建字符串仍然会使用堆内存。尽管方法返回时内存会被完全恢复,但在小内存微处理器上,如果没有必要,您应该避免在处理方法中创建额外的字符串。
避免创建字符串的规则
避免在循环中创建字符串()
在loop() 方法中创建的字符串具有相对较长的生命周期,因为它们在loop() 执行其工作并调用各种方法时存在。这些字符串应作为全局变量创建,或将其创建移至使用它们的方法,以便在方法返回时丢弃它们。请参阅 上面的 MemFrag 示例草图。
使用字符串引用(String&)作为参数,并且在编写方法时不返回字符串结果
为了避免在调用带有字符串参数和返回值的方法时使用额外的内存,请按如下方式编写
void addMeasurementToString(const String&units, float value, String&rtn) { rtn += String(value,3); // 浮点为 3 位数字。String(value,3) rtn +=units; }
需要注意的要点是
1) 将字符串作为引用传递,即String&。这避免了复制字符串参数所需的额外内存和时间
2)不应修改的参数应作为const String&传递,然后设置 IDE 首选项文件 → 首选项 → 编译器警告 == ALL,这样编译器就会警告你
警告:将“const String”作为“this”参数传递会丢弃限定符 [-fpermissive]
如果您的代码尝试修改该const String& 参数。
3) rtn 字符串,String& rtn,应该调用 reserve(),以便可以添加额外的文本而无需重新分配其大小。
除非别无选择,否则不要使用 String(..)
使用 += 或 concat() 代替,例如
rtn += 'c'; // 或 rtn += "s1"; // 或 rtn += num
唯一需要使用 String() 的情况是格式化浮点数或整数时。例如
rtn += String(f,3); // 浮点数保留 3 位小数(默认为 2 位) OR rtn += String(i,16); // 十六进制格式的整数
对于 == != 或 .equals(),你可以将其与字符串或“str”进行比较,而无需创建任何额外的字符串
使用“...”时字符串 <=、<、>=、> equalsIgnoreCase( )、startsWith( )、endsWith( )、indexOf( )、lastIndexOf( ) 和 replace( )
当将字符串与字符串(即“文本”)进行比较时,运算符 <=、<、>=、> 以及函数 equalsIgnoreCase()、startsWith()、endsWith()、indexOf()、lastIndexOf() 和 replace() 首先从“文本”创建一个字符串。如果您经常进行相同的比较/替换,您可能需要考虑为“文本”创建一个全局字符串,但通常没有必要。
Substring() 总是创建一个临时字符串
substring() 是唯一返回新字符串的 String 方法(除了构造函数之外)。因此,substring() 总是会创建一个临时字符串。但是,如果将返回的字符串分配给结果 String&,则立即丢弃临时返回的字符串并恢复其内存。
不要使用 + 运算符
不要使用 + 运算符连接字符串,因为它会在将其复制到结果字符串之前创建所有部分的完整字符串。因此,不要
rtn = "123"+sb+"345"; // 不要这样做
使用这些不会创建任何临时字符串的语句,并且当 rtn 保留了足够的空间时,不会使用额外的内存。
rtn = "123"; rtn += sb; rtn +="345";
避免使用 begin() 和 end()
String begin() 和 end() 函数允许对底层字符串缓冲区进行不受控制的访问,这使得您的代码很容易“破坏”字符串。因此,出于安全考虑,请勿在草图中使用 begin() 或 end()。
浙公网安备 33010602011771号