【问题标题】:Understanding cryptic C++ statement理解神秘的 C++ 语句
【发布时间】:2014-03-28 09:06:21
【问题描述】:

我来自使用 VB.NET 的 .NET 编程,这是我第一次为基于 Arduino 的应用程序进行 C 开发。

通读 Arduino 头文件(为__FlashStringHelper 寻找类似.ToString 的方法)我偶然发现了下一个#define

#define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))

首先我理解的是:

这是一个带有名为string_literal 的参数的宏,并且该参数以某种方式在括号内的表达式中使用。就是这样!

这些&lt;&gt;标志和那个*突然出现的指针标志是什么?

【问题讨论】:

  • 语言错误。那是 C++,不是 C。我已经编辑了标题和标签。
  • 您可以阅读有关reinterpret_cast for example here 的信息。简而言之,它是 C 风格转换的 C++ 变体。
  • 术语提示:#define 被称为预处理指令,整行是一个宏定义(对于函数式宏@987654332 @)。

标签: c++ arduino reinterpret-cast


【解决方案1】:

一般类型转换
在 C++ 中,有多种类型的强制转换可用于在类型之间进行转换。它们很重要,因为 C++ 是一种强类型语言,编译器不一定知道类型之间的任何给定转换是安全的或合理的。默认情况下,除非你明确告诉它要做什么,否则它会发出错误或警告。

所有 C++ 强制转换都具有以下格式:

x_cast <new_type> (expression)

x_cast 可以是以下之一:static_castdynamic_castreinterpret_castconst_cast

当您想要强制编译器在不相关的指针类型之间进行转换时,使用重新解释强制转换。结果是指向内存中完全相同的数据的指针。但是,它会被当作不同的类型来处理(或解释),这可以允许一些有趣的操作。

例如,假设您有一个指向 4 字节无符号整数的指针,并且您希望单独访问每个字节。您可以通过重新解释指向 1 字节类型的指针来做到这一点,如下所示:

uint32_t num = 12345;
uint32_t *p1 = &num;

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];

指针p1p2 都指向存储在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++ 有趣的方面之一——您实际上可以调用不存在的对象的方法,只要该对象不尝试访问不存在的成员数据。

【讨论】:

  • 非常感谢 Bloomfield 先生:非常清楚的解释,现在我更进一步了!
【解决方案2】:

reinterpret_cast操作符用于类型转换,将一种指针类型转换为另一种类型。

这里PSTR 被类型转换(转换)为__FlashStringHelper*

【讨论】:

    猜你喜欢
    • 2014-02-03
    • 2013-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-11
    • 2015-05-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多