在原始类型指针之间进行转换

时间:2013-02-08 12:59:12

标签: c++ language-lawyer strict-aliasing

以下定义明确:

char* charPtr = new char[42];
int* intPtr = (int*)charPtr;

charPtr++;
intPtr = (int*) charPtr;

intPtr未正确对齐(在两种情况中至少有一种情况下)。只是把它放在那里是非法的吗?是UB在任何阶段使用它吗?你怎么能用它,怎么不能用呢?

2 个答案:

答案 0 :(得分:3)

一般情况下,如果int的对齐要求大于char的对齐要求(通常为{1}},则结果未指定(5.2.10p7)。结果将是int *类型的有效值,因此它可以是例如打印为operator<<的指针或转换为intptr_t

因为结果具有未指定的值,除非由实现指定,否则它是未定义的行为以间接它并对生成的int左值执行左值到右值的转换(未评估的上下文除外)。转换回char *不一定是往返。

但是,如果原始char *本身是来自int *的演员的结果,则演员int *计为往返的后半部分;在这种情况下,定义了演员阵容。

特别是,在上面char *new[]表达式的结果的情况下,我们保证(5.3.4p10)char *指针适当地对齐{ {1}},只要int。因为sizeof(int) <= 42表达式从分配函数获得其存储,所以适用3.7.4.1p2;可以转换new[]指针 指向具有基本对齐要求的任何完整对象类型的指针,然后用于访问对象[...] ,强烈暗示该对象与5.3.4p10的注释一样,对于{{{ 1}} void *表达式返回的指针。在这种情况下,char *是指向未初始化的new[]对象的指针,因此在其间接上执行左值到右值的转换是未定义的(3.8p6),但是完全定义了对其间接的分配。 int *对象在分配的存储(3.7.4.1p2)中是,因此将int转换回int将产生每1.8p6的原始值。这不适用于递增的int *指针,除非char *它不是char *对象的地址。

答案 1 :(得分:1)

首先,当然:指针保证在对齐中 第一种情况(按§5.3.4/ 10和§3.7.4.1/ 2),可能是正确的 两种情况都是一致的。 (显然,如果sizeof(int) == 1,但是 即使不是这种情况,实施也不会 必须有对齐要求。)

为了清楚说明:你的演员都是reinterpret_cast

除此之外,这是一个有趣的问题,因为就此而言 我可以说,两个演员阵容没有区别 该标准是关注的。转换的结果是 未指明(根据§5.2.10/ 7);你甚至都不保证 将其转换回char*将导致。{ 原始价值。 (例如,显然不会在机器上 其中int*小于char*。)

在实践中,当然:标准要求退货 new char[N]的值与任何值充分对齐 可能适合它,所以你保证能够做到:

intPtr = new (charPtr) int;

这与你的演员有完全相同的效果,因为 int的默认构造函数是无操作。 (并假设 sizeof(int) <= 42。)所以很难想象一个实现 其中第一部分失败。你应该可以使用 intPtr就像其他任何合法获得的intPtr一样。而且 将它转换回char*的想法会以某种方式产生 显示与原始char*不同的值 荒谬的。

在第二部分,所有的赌注都是关闭的:你绝对不能 取消引用指针(除非您的实现保证 否则),也很可能将它转换回来 到char*会产生不同的结果。 (想象一句话 例如,已解决的机器,将char*转换为。{ int*四舍五入。然后转换回来会导致 char*比原始sizeof(int)高{{1}}。要么 其中一直试图转换未对齐的指针 在空指针中。)