使用 T[] 访问彼此相邻声明的两个 T 类型的对象是未定义的行为吗?

Is it undefined behaviour to access two objects of type T declared next to each other using T[]?

我最近观看了 Miro Kenjp 的 CppCon 演讲“不符合规范的 C++:委员会对你隐藏的秘密”。

在 28:30 (https://youtu.be/IAdLwUXRUvg?t=1710) 他说访问彼此相邻的双打是 UB,我不太明白为什么。有人可以解释为什么会这样吗?

如果是这种情况,那么它的 UB 肯定可以通过这种方式访问​​其他内容,例如:

int* twoInts = malloc(sizeof(int) * 2);
int secondInt = twoInts[1]; //Undefined behaviour?

不能保证变量会彼此相邻分配。

  1. malloc 使用的内存区域可能与临时、局部、变量的内存区域不同。

  2. 局部变量可能没有存储在内存中。它们可以存储在寄存器中。

  3. 访问声明范围之外的数组元素是未定义的行为。
    该数组可以在内存的末尾分配,因此在数组外部访问没有分配给它的内存。

  4. 局部变量可以定义在另一个内存段中,例如stack., 那跟动态分配的内存不一样

这是一个例子。
嵌入式系统具有片上存储器和“片上系统”(SOC)之外的存储器。片上内存速度更快,但数量更少。架构师将此内存分配给堆栈。 SOC外的内存比较慢,但是里面比较多,所以分配给了动态内存。

另一个例子:
操作系统支持虚拟内存。内存根据需要分页到硬盘驱动器上。操作系统为您的程序分配少量内存。少量内存会分配给栈,虚拟内存会分配给动态内存。

并非所有平台都是由连续内存构成的。内存位置也可以分配给硬件设备。

我想引用演示文稿中的一句话开始:

You are not programming against the CPU, you are programming against the abstract machine

你说:

He states that accessing doubles next to each other was UB

但是你的报价不完整。他具体说明了这个非常关键的事实:

... unless the objects are part of an array

malloc 是一个转移注意力的问题(和一袋另一套问题)。他的代码使用 new[] 所以 malloc 只是在给井下毒。

他在幻灯片中提到的具体问题是 buffer 上创建的 double 对象是由 std::uninitialized_default_construct_n 创建的,并且此方法不会创建双精度数组,而是创建多个在内存中连续的对象。他断言,在 C++ 标准(您正在编程的抽象机器)中,您不能将对象视为数组的一部分,除非您实际创建了对象数组。

作者试图提出的观点是 C++ 标准存在缺陷,并且没有严格符合标准的方法来创建灵活的数组(C++20 之前)。


参考代码(转载于图片后):

struct header
{
    int size;
    byte* buffer;
    thing some;
};
constexpr size_t x = ...;

byte* buffer = new byte[x + n * sizeof(double)];
header* p = new (buffer) header{n, buffer};
uninitialized_default_construct_n(
    reinterpret_cast<double*>(buffer + x), n);
double* data = reinterpret_cast<double*>(p->buffer + x);

data[0] = data[1] + data[2]; // <-- problem here
                             // because we never created an array of doubles