Skip to content

Arduino - 字符串

Arduino - 处理文本:String 对象与字符数组

Section titled “Arduino - 处理文本:String 对象与字符数组”

在编程中,处理文本是基础。无论您是在LCD上显示数据、从串口监视器解析命令,还是与Web API交互,您都需要处理字符串。在Arduino生态系统中,处理文本主要有两种方式,每种方式都有显著的权衡。

  • C风格字符串: 传统的、内存高效的方法,使用以空字符结尾的字符数组(char[])。
  • String 对象: 一种更用户友好、面向对象的类,它简化了许多文本操作任务。

理解两者的区别对于编写稳定可靠的Arduino代码至关重要,尤其是在内存有限的设备上。本章将引导您了解这两种方式,帮助您选择适合工作的正确工具。

这是继承自C语言的经典字符串处理方式。字符串简单来说就是以特殊“空字符”('\0',其ASCII值为0)结尾的字符数组。这个空字符标志着字符串的结束。

void setup() {
Serial.begin(9600);
// 编译器会自动添加空终止符 '\0'
char myMessage[] = "Hello, Arduino!";
Serial.println(myMessage);
// 查找字符串长度(不包括空终止符)
int len = strlen(myMessage);
Serial.print("Length: ");
Serial.println(len);
// 查找内存大小(包括空终止符)
int size = sizeof(myMessage);
Serial.print("Size in memory: ");
Serial.println(size); // 将是长度 + 1
}
void loop() {}

修改C风格字符串需要小心,以避免缓冲区溢出——即写入超出数组末尾,这会破坏内存并导致程序崩溃。务必使用strncpy()和strncat()等带有“n”的版本函数,它们限制了写入的字节数。

void setup() {
Serial.begin(9600);
// 创建一个足够大的缓冲区以容纳未来的文本
char buffer[50] = "Log: ";
// 安全地追加(连接)文本
// strncat(目标, 源, 要复制的最大字符数)
strncat(buffer, "System OK.", sizeof(buffer) - strlen(buffer) - 1);
Serial.println(buffer);
}
void loop() {}

String对象提供了一种更直观的方式来处理文本。它为您处理内存分配,并允许您使用+等简单运算符进行字符串连接。

void setup() {
Serial.begin(9600);
// 创建一个 String 对象
String greeting = "Hello, ";
// 简单连接
greeting += "World!";
Serial.println(greeting);
Serial.print("Length: ");
Serial.println(greeting.length());
// 其他有用的方法
greeting.replace("World", "Arduino");
Serial.println(greeting);
greeting.toUpperCase();
Serial.println(greeting);
}
void loop() {}

关键争议:String 对象 vs. char[] 数组

Section titled “关键争议:String 对象 vs. char[] 数组”

虽然String对象看起来更容易,但它的便利性伴随着一个隐藏的成本:动态内存分配。

类比: 想象一下您的Arduino的RAM是一个有编号停车位的小停车场。

  • char[] 就像为您的项目整个持续时间预留了一块相邻的车位(char buffer[50])。这些车位是您的,固定且可预测。
  • String 就像一个泊车员,每次您更改字符串时,他都会为您找一个空位。如果您频繁地更改字符串(例如,在循环中进行连接),泊车员可能不得不先停好您的车,然后把它移到一个更大的位置,从而留下旧的、小的空位。随着时间的推移,停车场会充满停放车辆之间小的、无法使用的空位。这被称为堆碎片。

当您的Arduino由于碎片化而耗尽连续的内存块时,它将无法分配更多内存,您的程序将不可预测地崩溃或行为异常。这对于需要长时间可靠运行的项目尤其危险。

特性char 数组 (C风格字符串)String 对象
内存使用静态,可预测。在编译时定义大小。动态。在运行时分配和释放内存。
稳定性高。无碎片化风险。不小心易发生缓冲区溢出。在长时间运行的草图中稳定性低。 可能导致堆碎片并引发崩溃。
易用性更复杂。需要strcpy、strcat等函数。非常容易。使用直观的运算符(+)和方法(.concat(),.replace())。
性能通常更快,因为没有内存管理开销。由于内存分配和释放开销,略慢。
  • 对于快速测试、简单草图或一次性文本处理: String对象完全没问题,可以加快开发速度。
  • 对于任何需要可靠、长时间运行或在RAM非常少的设备上运行的项目: 强烈建议使用char数组。 学习使用固定大小的缓冲区和安全函数,如snprintf()、strncpy()和strncat()。
  • 不惜一切代价避免在loop()内部进行String连接(+或+=)。 这是导致堆碎片的最快方式。
  • 如果您必须使用String对象, 请在其setup()方法中调用reserve()方法预分配缓冲区。这可以减少,但不能完全消除碎片化问题:myString.reserve(100);