8

这可能会导致未定义的行为吗?

uint8_t storage[4];

// We assume storage is properly aligned here.
int32_t* intPtr = new((void*)storage) int32_t(4);

// I know this is ok:
int32_t value1 = *intPtr;
*intPtr = 5;

// But can one of the following cause UB?
int32_t value2 = reinterpret_cast<int32_t*>(storage)[0];
reinterpret_cast<int32_t*>(storage)[0] = 5;

char具有严格混叠的特殊规则。如果我使用char而不是 uint8_t它仍然是未定义的行为吗?还有什么变化?

正如成员 DeadMG 指出的那样,reinterpret_cast依赖于实现。如果我改用 C 风格的演员(int32_t*)storage表,会发生什么变化?

4

2 回答 2

7

当引入别名考虑时,placement new 返回的指针可能与任何其他指针一样引起 UB。你有责任确保你放置对象的内存不会被任何不应该的东西所别名。

在这种情况下,您不能假设它uint8_t是一个别名char,因此应用了特殊的别名规则。此外,使用数组uint8_t而不是使用数组是毫无意义的,char因为sizeof()char, not而言是uint8_t。您必须自己计算大小。

另外,reinterpret_cast的效果完全是实现定义的,所以代码肯定没有明确定义的意思。

要实现低级不愉快的内存黑客,原始内存只需要使用、 和的别名char*,在这种情况下,最终目标类型在哪里,加上您可以从 a 获得的任何其他内容,例如 if是派生类和您将该派生类指针转换为指向基类的指针。任何其他都违反了严格的混叠和你好鼻恶魔。void*T*TintT*T

于 2014-01-08T00:43:33.590 回答
6

您使用通常放置 new 的版本确实很好。

§§ 3.8/1 和 3.8/4有一个解释1,其中平凡类型的对象能够按需“消失”和“出现”。这不是允许无视别名规则的免费通行证,因此请注意:

std::uint16_t storage[2];
static_assert( /* std::uint16_t is not a character type */ );
static_assert( /* storage is properly aligned for our purposes */ );

auto read = *reinterpret_cast<std::uint32_t*>(&storage);
// At this point either we’re attempting to read the value of an
// std::uint16_t object through an std::uint32_t glvalue, a clear
// strict aliasing violation;
// or we’re reading the indeterminate value of a new std::uint32_t
// object freshly constructed in the same storage without effort
// on our part

另一方面,如果您在第二个片段中交换了演员表(即先重新解释和编写),那么您也不完全安全。虽然在解释下,您可以证明写入发生在std::uint32_t隐式重用存储的新对象上,但后续读取的形式为

auto value2 = *reinterpret_cast<int32_t*>(storage);

§3.8/5 说(强调我的并且非常相关):

[…] 在对象的生命周期结束之后,在对象占用的存储空间被重用或释放之前,任何指向对象将要或曾经位于的存储位置的指针都可以使用,但只能以有限的方式使用。[…] 这样的指针是指分配的存储空间(3.7.4.2),并且使用指针就像指针是 type 一样void*,是明确定义的。

§3.8/6 是相同的,但采用引用/glvalue 形式(可以说更相关,因为我们在这里重用名称而不是指针,但该段落更难以脱离上下文理解)。另请参阅§3.8/7,它提供了一些我认为不适用于您的情况的有限余地。

为了简单起见,剩下的问题是:

T object;
object.~T();
new (&object) U_thats_really_different_from_T;
&object;                     // Is this allowed? What does it mean?
static_cast<void*>(&object); // Is this?

如果存储的类型碰巧涉及普通或无符号字符类型(例如,您storage确实有 type unsigned char[4]),那么我会说您有理由证明形成指向新对象存储的指针/引用是正确的(以后可能会重新解释)。再次参见例如¶¶ 5 和 6,它们具有用于形成指针/引用/glvalue 的显式转义子句和 §1.8描述对象如何涉及组成字节数组的C++ 对象模型。管理指针转换的规则应该简单明了且没有争议(至少相比之下……)。


1:很难衡量这种解释在社区中的接受程度——我在 Boost 邮件列表中看到了它,那里对它持怀疑态度

于 2014-01-08T07:10:40.390 回答