何时以及如何允许转换为字符指针

When and how is conversion to char pointer allowed?

本文关键字:字符 指针 转换 何允许 何时      更新时间:2023-10-16

我们可以通过将指向该对象的T*转换为char*来查看T该对象的表示形式。至少在实践中:

int x = 511;
unsigned char* cp = (unsigned char*)&x;
std::cout << std::hex << std::setfill('0');
for (int i = 0; i < sizeof(int); i++) {
std::cout << std::setw(2) << (int)cp[i] << ' ';
}

这将在我的系统上输出511的表示形式:ff 01 00 00

这里(肯定)发生了一些实现定义的行为。哪些演员允许我将int*转换为unsigned char*,该转换需要哪些转换?我是否在投掷后立即调用未定义的行为?我可以像这样投射任何T*类型吗?这样做时我可以依靠什么?

哪个演员允许我将int*转换为unsigned char*

在这种情况下,C型转换与reinterpret_cast<unsigned char*>相同。

我可以像这样铸造任何 T* 类型吗?

是和不是。yes 部分:可以安全地将任何指针类型转换为char*unsigned char*(使用适当的const和/或volatile限定符)。结果是实现定义的,但它是合法的。

no 部分:该标准明确允许char*unsigned char*作为目标类型。但是,您不能(例如)安全地将double*转换为int*。这样做,你就跨越了从实现定义的行为到未定义行为的边界。它违反了严格的别名规则。

您的演员表映射到:

unsigned char* cp = reinterpret_cast<unsigned char*>(&x);

int的基础表示形式是实现定义的,将其视为字符可以检查这一点。 在您的情况下,它是 32 位小端序。

这里没有什么特别之处 - 这种检查内部表示的方法对任何数据类型都有效。

C++03 5.2.10.7:指向对象的指针可以显式转换为指向不同类型的对象的指针。 除了将类型为"指向 T1 的指针"的右值转换为"指向 T2 的指针"类型(其中 T1 和 T2 是对象类型,T2 的对齐要求不比 T1 的对齐要求更严格)并转换回其原始类型会生成原始指针值之外,未指定此类指针转换的结果。

这表明强制转换会导致未指定的行为。 但实际上,从任何指针类型转换为char*将始终允许您检查(和修改)引用对象的内部表示形式。

在这种情况下,C 样式的强制转换等效于 reinterpret_cast。该标准描述了 5.2.10 中的语义。具体而言,在第7段中:

"指向对象的指针可以显式转换为指向 不同的对象类型.70 当类型为"指向 T1 的指针"的 prvalue v 为 转换为类型"指向 cvT2 的指针",结果为static_cast<cvT2*>(static_cast<cvvoid*>(v))T1 和 T2 是否 标准布局类型 (3.9) 和 T2 的对齐要求是 不比T1严格。将类型为 "指针到 T1"到类型"指向 T2 的指针"(其中 T1 和 T2 是对象类型,并且 T2的对准要求不比T2更严格 T1) 并返回到其原始类型将生成原始指针值。 任何其他此类指针转换的结果都是未指定的。

在您的情况下,这意味着什么,满足对齐要求,并且未指定结果。

示例中的实现行为是系统的字节序属性,在本例中,CPU 有点字节序。
关于类型转换,当您强制转换int*char*您正在执行的所有操作时,告诉编译器将cp指向的内容解释为 char,因此它将仅读取第一个字节并将其解释为字符。

指针之间的强制转换本身总是可能的,因为所有指针都只不过是内存地址,并且内存中的任何类型都可以始终被视为字节序列。

但是 - 当然 - 序列的形成方式取决于分解类型在内存中的表示方式,这超出了C++规范的范围。

也就是说,除非是非常病态的情况,否则您可以期望同一编译器为同一平台(或系列)的所有机器生成的所有代码上的表示形式是相同的,并且您不应该期望在不同平台上获得相同的结果。

一般来说,要避免的一件事是将类型大小之间的关系表示为"预定义": 在您的样本中,您假设sizeof(int) == 4*sizeof(char):这不一定总是正确的。

但是 sizeof(T) = N*sizeof(char) 总是正确的,因此无论 T 如何,都可以始终被视为 char-s 的整数

除非你有一个强制转换运算符,否则强制转换只是告诉以不同的方式"看到"该内存区域。没什么特别的,我会说。

然后,您正在逐字节读取内存区域;只要您不更改它,它就可以了。当然,您看到的结果在很大程度上取决于平台:考虑字节序、字大小、填充等。

只需反转字节顺序,然后它就变成了

00 00 01 ff

即 256 (01) + 255 (ff) = 511

这是因为您的平台是小端序。