一般类型转换
在 C++ 中,有多种类型的强制转换可用于在类型之间进行转换。它们很重要,因为 C++ 是一种强类型语言,编译器不一定知道类型之间的任何给定转换是安全的或合理的。默认情况下,除非你明确告诉它要做什么,否则它会发出错误或警告。
所有 C++ 强制转换都具有以下格式:
x_cast <new_type> (expression)
x_cast 可以是以下之一:static_cast、dynamic_cast、reinterpret_cast 或 const_cast。
当您想要强制编译器在不相关的指针类型之间进行转换时,使用重新解释强制转换。结果是指向内存中完全相同的数据的指针。但是,它会被当作不同的类型来处理(或解释),这可以允许一些有趣的操作。
例如,假设您有一个指向 4 字节无符号整数的指针,并且您希望单独访问每个字节。您可以通过重新解释指向 1 字节类型的指针来做到这一点,如下所示:
uint32_t num = 12345;
uint32_t *p1 = #
uint8_t *p2 = reinterpret_cast<uint8_t*>(p1);
// Access the individual bytes:
uint8_t byte0 = p2[0];
uint8_t byte1 = p2[1];
uint8_t byte2 = p2[2];
uint8_t byte3 = p2[3];
指针p1 和p2 都指向存储在num 变量中的数据。不同之处在于,通过p2 访问它会导致编译器将其视为 1 字节无符号整数(而不是原始的 4 字节类型)。这使您可以在原始变量的不同位置提取/操作单个字节。
对于像这样的简单示例,reinterpret_cast 非常安全。然而,在很多情况下,如果不小心使用它,它可能会出错,或者根本没有用处。例如,尝试将float 指针重新解释为int。就其本身而言,它不会做任何坏事。但是,结果将完全没有用,因为如果您尝试像 int 那样处理它,float 的底层二进制表示就没有意义。
同样的方法也适用于对象,让您可以解释一个类的实例,就好像它是另一个类的实例一样。但是,它不会进行任何智能转换。它只是强制以不同的方式处理原始二进制数据,这意味着您必须非常确信重新解释是有意义的。
阿杜诺
您在 Arduino 文件中确定的行在完全展开时相当复杂,因此我们将对其进行分解。正如我认为您已经确定的那样,它定义了一个名为 F() 的宏,并且该宏接受一个名为 string_literal 的参数。
顾名思义,它旨在与字符串文字F("like this") 一起使用。在表面之下,编译器将字符串文字视为指向字符数组的指针。或者换句话说,char *。
在F() 宏中,字符串文字被放入另一个名为PSTR() 的宏中。这基本上添加了一大堆其他东西,告诉编译器将字符串数据存储在程序空间(草图位于您的 Arduino 上)而不是 SRAM(变量位于哪里)。
此时,reinterpret_cast 开始发挥作用。 PSTR() 中的所有内容都很重要,但并不会真正影响演员看到的数据类型。你基本上可以想象它的行为是这样的:
char *ptr = "my string data";
reinterpret_cast<const __FlashStringHelper *>(ptr);
__FlashStringHelper 是一个类,这意味着它的类型与char * 无关。这就是为什么我们需要重新解释它,以便编译器知道我们对操作的安全性负责。当使用强制转换的结果时,它就像一个指向 __FlashStringHelper 对象的指针,这意味着它的方法可以用来访问/处理字符串数据。
实际上,没有实际创建 __FlashStringHelper 的实例。底层数据仍然只是我们的字符串文字。这是 C++ 有趣的方面之一——您实际上可以调用不存在的对象的方法,只要该对象不尝试访问不存在的成员数据。