[英]What is the memory layout of vector of arrays?
任何人都可以解释一下记忆的布局
std::vector<std::array<int, 5>> vec(2)
它是否提供具有2行5个元素的2D数组的连续内存块?
据我所知,矢量矢量
std::vector<std::vector<int>> vec(2, std::vector<int>(5))
提供存储器中不同位置的两个 长度为 5个元素的连续数组的存储器布局。
对于数组的向量是否相同?
数组没有任何间接,但只是“直接”存储它们的数据。 也就是说, std::array<int, 5>
实际上包含连续的五个int
,flat。 并且,与向量一样,它们不会在元素之间添加填充,因此它们“内部连续”。
但是, std::array
对象本身可能比其元素集大 ! 允许有像填充这样的尾随“东西”。 所以,尽管有可能,但不一定是正确的,你的数据将全部在第一种情况下连续的。
An int
+----+
| |
+----+
A vector of 2 x int
+----+----+----+-----+ +----+----+
| housekeeping | ptr | | 1 | 2 |
+----+----+----+-----+ +----+----+
| ^
\-----------
An std::array<int, 5>
+----+----+----+----+----+----------->
| 1 | 2 | 3 | 4 | 5 | possible cruft/padding....
+----+----+----+----+----+----------->
A vector of 2 x std::array<int, 5>
+----+----+----+-----+ +----+----+----+----+----+----------------------------+----+----+----+----+----+----------->
| housekeeping | ptr | | 1 | 2 | 3 | 4 | 5 | possible cruft/padding.... | 1 | 2 | 3 | 4 | 5 | possible cruft/padding....
+----+----+----+-----+ +----+----+----+----+----+----------------------------+----+----+----+----+----+----------->
| ^
\-----------
而且,即使它是由于别名规则,你是否能够使用单个int*
来导航所有10个数字可能是另一回事!
总而言之,十个int
的向量将更清晰,更完整,并且可能更安全。
在向量向量的情况下,向量实际上只是一个指针加上一些内务处理,因此间接(如你所说)。
之间的大的差别std::vector
和std::array
是std::vector
包含一个指向它包装存储器,而std::array
包含在本身的实际阵列。
这意味着矢量矢量就像一个锯齿状阵列 。
对于数组向量, std::array
对象将连续放置,但与向量对象分开。 请注意, std::array
对象本身可能比它们包含的数组大,如果是这样,那么数据将不是连续的。
最后的位还意味着阵列(纯C样式或std::array
)的std::array
也可以不保持数据连续。 std::array
中的std::array
对象将是连续的,但不是数据。
保证“多维”数组的连续数据的唯一方法是嵌套的普通C风格数组。
C ++标准不保证std::array
在数组末尾不包含任何有效负载,因此你不能假设后续数组的第一个元素就在前一个数组的最后一个元素之后。
即使是这种情况,尝试通过指针算法在指向不同数组中的元素的指针上到达数组中的任何元素的行为也是未定义的。 这是因为指针算法仅在数组中有效。
以上也适用于std::array<std::array>
。
static_assert(sizeof(std::array<int,5>)==5*sizeof(int));
以上缓解了在std::array
末尾有任何填充的问题。 没有主要的编译器会导致上述内容未能达到此日期,我打赌将来也不会。
当且仅当上述内容失败时, std::vector<std::array<int,5>> v(2)
将在std::array
s之间产生“间隙”。
这没有你想要的那么多; 生成的指针如下:
int* ptr = &v[0][0];
只有ptr+5
的有效域,解除引用ptr+5
是未定义的行为。
这是由于别名规则造成的; 即使您知道它在那里,也不允许“走”过一个对象的末尾,即使您知道它在那里,除非您首先往返于允许较少限制指针算术的某些类型(如char*
)。
反过来,该规则允许编译器通过哪个指针来推断正在访问哪些数据,而不必证明任意指针算法将允许您到达外部对象。
所以:
struct bob {
int x,y,z;
};
bob b {1,2,3};
int* py = &b.y;
无论你用py
做什么作为int*
,你都不能用它合法地修改x
或z
。
*py = 77;
py[-1]=3;
std::cout << b.x;
编译器可以优化std::cout
行以简单地打印1
,因为py[-1]=3
可能会尝试修改bx
,但通过这种方式这样做是未定义的行为。
同样的限制会阻止你从std::vector
的第一个数组转到第二个数组(即超出ptr+4
)。
创建ptr+5
是合法的,但仅作为一个过去的指针。 比较ptr+5 == &v[1][0]
也未在结果中指定,即使它们的二进制值在每个主要硬件系统上的每个编译器中绝对相同。
如果你想进一步深入兔子洞,由于对指针别名的这些限制,甚至不可能在C ++本身内实现std::vector<int>
。 最后我检查了(这是在c ++ 17之前,但我没有在C ++ 17中看到解决方案)标准委员会正在努力解决这个问题,但我不知道任何此类努力的状态。 (这不是你想象的问题,因为没有什么要求std::vector<int>
在符合标准的C ++中实现;它必须只具有标准定义的行为。它可以在内部使用特定于编译器的扩展。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.