Arduino - 数据类型
Arduino - 现代 C++ 中的数据类型
Section titled “Arduino - 现代 C++ 中的数据类型”在为 Arduino 编程时,您主要使用的是 C++ 语言。数据类型是 C++ 中声明变量和函数的基本概念。变量的类型决定了它在内存中的大小以及存储的位(bit)如何被解释。在像 Arduino 这样的内存受限微控制器上,选择正确的数据类型至关重要。
本指南涵盖了 Arduino 开发中基本的数据类型,并更新了现代的最佳实践。
核心数据类型概述
Section titled “核心数据类型概述”以下是 Arduino 环境中可用基本数据类型的总结:
| 类型 | 大小 (Uno/AVR 板上) | 范围 / 描述 |
|---|---|---|
| boolean | 1 byte | 存储 true 或 false。 |
| byte | 1 byte | 存储 0 到 255 的无符号数字。 |
| char | 1 byte | 存储字符或 -128 到 127 之间的小整数。 |
| unsigned char | 1 byte | 存储 0 到 255 的无符号数字(与 byte 相同)。 |
| int | 2 bytes | 存储 -32,768 到 32,767 之间的整数。 |
| unsigned int | 2 bytes | 存储 0 到 65,535 之间的无符号整数。 |
| long | 4 bytes | 存储 -2,147,483,648 到 2,147,483,647 之间的大整数。 |
| unsigned long | 4 bytes | 存储 0 到 4,294,967,295 之间的大无符号整数。 |
| float | 4 bytes | 存储单精度浮点数。 |
| double | 4 bytes | 在基于 AVR 的板(Uno、Mega)上与 float 相同。在现代 ARM 板(Due、Portenta)上为 8 字节。 |
| void | N/A | 用于表示函数不返回任何值。 |
关键数据类型深入解析
Section titled “关键数据类型深入解析”void 关键字专门用于函数声明中,以指定函数不返回任何值。内置的 setup() 和 loop() 函数就是经典的例子。
void setup() { // 这个函数只运行一次,不返回任何值。}boolean
Section titled “boolean”boolean 类型存储两个值之一:true 或 false。它非常适合跟踪状态,例如 LED 是开还是关。它占用一个字节的内存。
const int ledPin = 13;boolean isLedOn = false;
void setup() { pinMode(ledPin, OUTPUT);}
void loop() { if (isLedOn) { digitalWrite(ledPin, LOW); isLedOn = false; } else { digitalWrite(ledPin, HIGH); isLedOn = true; } delay(1000);}char 和算术运算
Section titled “char 和算术运算”char 类型使用一个字节的内存存储字符值。字符字面量用单引号括起来(例如 'A')。在内部,字符根据 ASCII 表存储为数字。这允许对它们进行算术运算。
// 'A' 的 ASCII 值为 65。char myChar = 'A';
// 这将得到字符 'C',其 ASCII 值为 67。char nextChar = myChar + 2;
Serial.print(nextChar); // Prints "C"最佳实践:固定宽度整数
Section titled “最佳实践:固定宽度整数”嵌入式系统中一个常见问题是 int 类型的大小可能因架构而异(例如,在 Arduino Uno 上为 2 字节,在 Arduino Due 上为 4 字节)。这在移植代码时可能导致意外行为。
为避免此问题,现代 C++ 实践鼓励使用 固定宽度整数 从 <stdint.h> 头文件。这些类型明确声明了它们的大小。
int8_t/uint8_t:8 位有符号/无符号整数(等同于char和byte)。int16_t/uint16_t:16 位有符号/无符号整数(等同于 Uno 上的int)。int32_t/uint32_t:32 位有符号/无符号整数(等同于long)。int64_t/uint64_t:64 位有符号/无符号整数。
#include <stdint.h>
// 这个变量在任何 Arduino 板上都将是 16 位。uint16_t sensorValue = 1023;字符串:String 对象与 C 风格字符数组
Section titled “字符串:String 对象与 C 风格字符数组”在 Arduino 中处理文本可以通过两种方式完成,每种方式都有显著的权衡。
1. C 风格字符串(char 数组)
Section titled “1. C 风格字符串(char 数组)”这是传统的、内存高效的方式。字符串是一个以空终止符('\0')结尾的字符数组。它们速度快,不会导致内存碎片,但操作起来可能更复杂。
char message[16] = "Hello, world!"; // 16 个字符的数组,其中 15 个用于文本,1 个用于空终止符。Serial.println(message);2. Arduino String 对象
Section titled “2. Arduino String 对象”String 对象提供了易于使用的文本操作方法(连接、搜索等)。然而,它使用动态内存分配。在小型微控制器上,频繁创建和销毁 String 对象可能导致堆碎片,最终导致程序意外崩溃。
String message = "Hello, ";String name = "Arduino";
// 易于连接,但在幕后使用动态内存。String greeting = message + name;Serial.println(greeting);对于小型、稳定的程序(sketch),String 对象可能很方便。对于任何大型、长期运行或任务关键型应用,强烈建议使用 C 风格字符数组以避免内存问题。
复合类型和用户定义类型
Section titled “复合类型和用户定义类型”数组是相同类型变量的集合。
int sensorReadings[5]; // 一个用于存储 5 个整数读数的数组。sensorReadings[0] = analogRead(A0);sensorReadings[1] = analogRead(A1);struct 允许您将不同数据类型分组到一个单一的用户定义类型中。这对于组织相关数据非常有用。
struct SensorData { int id; float temperature; float humidity; boolean isActive;};
void setup() { Serial.begin(9600);
SensorData livingRoomSensor; livingRoomSensor.id = 1; livingRoomSensor.temperature = 22.5; livingRoomSensor.humidity = 45.8; livingRoomSensor.isActive = true;
Serial.print("Sensor ID: "); Serial.println(livingRoomSensor.id); Serial.print("Temperature: "); Serial.println(livingRoomSensor.temperature);}常见错误和调试
Section titled “常见错误和调试”- 整数溢出(Integer Overflow):将超出其范围的值赋给数值变量。例如,
int count = 40000;将会溢出并导致一个负数。请使用更大的类型,如unsigned int或long。 - 有符号/无符号不匹配(Signed/Unsigned Mismatch):对有符号和无符号数字进行算术运算可能导致意外结果,尤其是在比较时。
- 浮点数不精确(Floating Point Imprecision):
float和double是近似值。不要直接使用==比较它们。相反,应检查其绝对差值是否在很小的容差范围内:if (abs(a - b) < 0.001) { ... }。