arduino简单string入门——使用 StringReserveCheck查找并消除漏洞

要检测何时未预留足够的空间,可以使用StringReserveCheck 类。下载StringReserveCheck.zip 文件并使用 IDE Sketch → Include Library → Add .ZIP library... 安装它。

然后,您可以添加 StringReserveCheck 对象来跟踪每个长寿命字符串。StringReserveCheck 的使用方法如下:-

在要检查的每个字符串之后立即分配一个 StringReserveCheck 对象。例如对于全局字符串

#include <StringReserveCheck.h>
String string1;
StringReserveCheck string1Check;

这确保了 String 和 StringReserveCheck 对象具有相同的范围和生命周期。

在 setup() 中,在为字符串保留空间之后,立即调用其 StringReserveCheck 对象上的init() 来捕获初始状态。init () 还允许您指定在何处写入 checkReserve 消息。例如

void setup() {
  . . .
  string1.reserve(32);
  string1Check.init(string1); // checkReserve( ) will not print any msgs, you need to check its return value
  string2.reserve(32);
  string2Check.init(string2, Serial); // init( ) will print a msg if memory is low and checkReserve( ) will print a msg
  if (!string3.reserve(32)) { // check the last largest reserve
    while (1) { // stop here and print repeating msg
      Serial.println(F("Strings out-of-memory"));
      delay(3000); // repeat msg every 3 sec
    }
  }
  if (!string3Check.init(string3, Serial)) {    // check return for low memory
    Serial.println(F("Memory Low after reserves()"));
  }
  . . . 
}

如果内存不足,stringCheck.init(string, Serial); 将打印一条警告消息并返回 false。stringCheck.init (string); (没有 Serial 参数) 不会打印任何消息,因此您需要检查返回值。

然后,当你想检查储备时,你可以使用stringCheck.checkReserve() 。例如

void loop() {
  float temp = 27.35;
  printDegC(temp, string2);
  Serial.println(string2);
  string1Check.checkReserve(); // init(string1,Serial); was specified so msg printed
  if (!string2Check.checkReserve()) { // check return,  init(string2,Serial); was specified so msg also printed
    Serial.print(F("string2 reserve too small. Current length():")); Serial.println(string2.length());
  }
  if (!string3Check.checkReserve()) { // init(string3); // no output specified, check return
    Serial.print(F("string3 reserve too small. Current length():")); Serial.println(string3.length());
  }
  Serial.println();
}

MemFrag_ex3.ino 已添加这些StringReserveCheck。MemFrag_ex3.ino输出 为

内存碎片示例 3
这是锅炉房的温度 27.4 摄氏度
!!reserve() 对字符串 'This is th...' 来说不够大 当前长度:51 
string2 保留太小。当前长度():51 
reserve() 对字符串 'string3 in...' 来说足够大 当前长度:21

此输出清楚地表明 string2 的保留需要增加,并且当前长度提示了新的保留应该是多少。string3 的保留正常。打印该消息是因为在 string3Check.init(string3,Serial) 语句中将 Serial 设置为输出。没有为 string1 打印任何消息,因为string1Check.init(string1); 没有指定将消息发送到何处。

摘要:使用StringReserveCheck 类检查初始保留是否足够大以用于应用程序。如果 checkReserve() 返回 true,则表示此字符串没有因重新分配而产生碎片。

当 UNO 或 Mega2560 耗尽堆内存时会发生什么情况

当堆栈与堆发生冲突时,内存不足是致命的。如上面的 SRAM 图所示,每次调用方法时,堆栈都会为方法返回、方法参数和任何方法局部变量使用更多空间。堆栈不会检查堆在哪里,只会占用所需的空间。结果是,当内存耗尽时,堆栈会覆盖现有的堆对象,从而导致内存损坏和程序崩溃。

Arduino UNO 只有 2048 字节的 SRAM,因此很容易在该板上出现内存不足的情况。只需使用“text... ”而不是 F(“text... ”) 即可。但是,当您在 UNO 或 Mega2560 和其他基于 AVR 的板上使用字符串时,由于内置了 128 字节的__malloc_margin ,它们在堆内存不足的情况下非常稳定。堆栈始终至少有那么多空间可用于保持程序运行。

MemFrag_ex4.ino中,string1 和 string3 的保留大小已针对 UNO 进行了仔细调整,以便几乎所有内存都得到使用。然后,当 sketch 尝试将 string2 从其保留的 32 重新分配为更大的值以处理更长的标题时,它会耗尽内存。

void setup() {
  . . .
  Serial.println(F("Memory Fragementation Example 4 - Out Of Memory"));
  string2.reserve(32); // do reserve in order smallest first string2
  string2Check.init(string2, Serial); // init( ) will print a msg if memory is low and checkReserve( ) will print a msg
  string1.reserve(600); // next largest string1
  string1Check.init(string1); // checkReserve( ) will not print any msgs, you need to check its return value
  if (!string3.reserve(930)) { // do largest last and check its return
    while (1) { // stop here and print repeating msg
      Serial.println(F("Strings out-of-memory"));
      delay(3000); // repeat msg every 3 sec
    }
  }
  if (!string3Check.init(string3, Serial)) {    // check return for low memory
    Serial.println(F("Memory Low after reserves()"));
  }
  . . .
}

Then when the MemFrag_ex4.ino sketch is run the output is

!! Low memory available, stability problems may occur.
Memory Low after reserves()
Memory Fragementation Example 4 - Out Of Memory
27.4degC
!! reserve() NOT large enough for String '27.4degC' Current len:8
string2 reserve too small. Current length():8
reserve() large enough for String 'string3 in...' Current len:21

27.4degC
!! reserve() NOT large enough for String '27.4degC' Current len:8
string2 reserve too small. Current length():8
reserve() large enough for String 'string3 in...' Current len:21

 

即使 UNO 内存不足,无法使 string2 足够大以添加标题,草图仍然保持稳定运行并且不会崩溃。

输出应该是
这是锅炉房的温度 27.4 摄氏度,
但现在缺少标题。27.4
摄氏度

意事项:
1) 草图持续运行,因为在 UNO 和 Mega2560 上,堆内存分配始终保持 128 字节的__malloc_margin,因此堆栈始终至少有那么多空间可用于保持程序运行。如果您有大量嵌套方法调用和大量局部变量,则可能会导致草图崩溃,但这种情况极其罕见。2
) 在方法中使用本地字符串比使用 char[] 更安全,因为如果内存不足,本地字符串内存分配就会失败,并且始终至少留下 128 个字节的空闲空间。而本地 char[] 堆栈分配永远不会失败,它们只会覆盖堆并导致内存损坏。

总结:当您使用字符串时,UNO 和 Mega2560 以及其他基于 AVR 的主板在内存不足的情况下非常稳定。草图继续运行,但字符串会丢失一些文本。

 

posted @ 2025-01-27 00:16  mcwhirr  阅读(50)  评论(0)    收藏  举报