24

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

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

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

4

7 回答 7

19

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

在这种情况下,C 样式转换与reinterpret_cast<unsigned char*>.

我可以像这样投射任何 T* 类型吗?

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

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

于 2012-12-21T19:26:00.673 回答
6

您的演员表映射到:

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

an 的底层表示int是实现定义的,并且将其视为字符允许您检查它。在您的情况下,它是 32 位小端。

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

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

这表明强制转换会导致未指定的行为。但务实地说,从任何指针类型转换为char*总是允许您检查(和修改)被引用对象的内部表示。

于 2012-12-21T19:18:28.773 回答
3

您的示例中的实现行为是您系统的字节序属性,在这种情况下,您的 CPU 是一个小字节序。
关于类型转换,当您int*char*所有正在做的事情进行转换时,就是告诉编译器将cp指向的内容解释为字符,因此它将仅读取第一个字节并将其解释为字符。

于 2012-12-21T19:18:07.327 回答
3

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

“指向对象的指针可以显式转换为指向不同对象类型的指针。70 当类型为“指向 T1 的指针”的纯右值 v 转换为类型“指向 cvT2”的指针时,结果是 static_cast<cvT2*>(static_cast<cvvoid*>(v))如果 T1 和 T2是标准布局类型 (3.9),T2 的对齐要求不比 T1 严格。将类型“指向 T1”的纯右值转换为类型“指向 T2”的指针(其中 T1 和 T2 是对象类型,其中“

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

于 2012-12-21T19:26:53.710 回答
1

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

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

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

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

但是 sizeof(T) = N*sizeof(char) 总是正确的,因此无论 T 总是可以看作是 char-s 的整数

于 2012-12-21T19:19:07.463 回答
0

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

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

于 2012-12-21T19:22:35.810 回答
0

只需颠倒字节顺序,它就变成了

00 00 01 ff

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

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

于 2012-12-21T19:26:02.277 回答