Arduino - 字符串
Arduino - 处理文本:String 对象与字符数组
Section titled “Arduino - 处理文本:String 对象与字符数组”在编程中,处理文本是基础。无论您是在LCD上显示数据、从串口监视器解析命令,还是与Web API交互,您都需要处理字符串。在Arduino生态系统中,处理文本主要有两种方式,每种方式都有显著的权衡。
- C风格字符串: 传统的、内存高效的方法,使用以空字符结尾的字符数组(
char[])。 String对象: 一种更用户友好、面向对象的类,它简化了许多文本操作任务。
理解两者的区别对于编写稳定可靠的Arduino代码至关重要,尤其是在内存有限的设备上。本章将引导您了解这两种方式,帮助您选择适合工作的正确工具。
1. C风格字符串(char 数组)
Section titled “1. C风格字符串(char 数组)”这是继承自C语言的经典字符串处理方式。字符串简单来说就是以特殊“空字符”('\0',其ASCII值为0)结尾的字符数组。这个空字符标志着字符串的结束。
示例:声明与使用
Section titled “示例:声明与使用”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() {}2. Arduino String 对象
Section titled “2. Arduino String 对象”String对象提供了一种更直观的方式来处理文本。它为您处理内存分配,并允许您使用+等简单运算符进行字符串连接。
示例:简单方便
Section titled “示例:简单方便”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())。 |
| 性能 | 通常更快,因为没有内存管理开销。 | 由于内存分配和释放开销,略慢。 |
最佳实践和建议
Section titled “最佳实践和建议”- 对于快速测试、简单草图或一次性文本处理:
String对象完全没问题,可以加快开发速度。 - 对于任何需要可靠、长时间运行或在RAM非常少的设备上运行的项目: 强烈建议使用
char数组。 学习使用固定大小的缓冲区和安全函数,如snprintf()、strncpy()和strncat()。 - 不惜一切代价避免在
loop()内部进行String连接(+或+=)。 这是导致堆碎片的最快方式。 - 如果您必须使用
String对象, 请在其setup()方法中调用reserve()方法预分配缓冲区。这可以减少,但不能完全消除碎片化问题:myString.reserve(100);