谁能解释一下
std::vector<std::array<int, 5>> vec(2)
是否提供2D阵列的连续存储块 有2行5个元素?
据我所知,向量的向量
std::vector<std::vector<int>> vec(2, std::vector<int>(5))
在内存中提供两个 长度为5个元素的 个连续数组的长度。
数组的向量会相同吗?
答案 0 :(得分:58)
数组没有任何间接的,只是“直接”存储它们的数据。也就是说,std::array<int, 5>
字面连续包含五个int
。而且,像矢量一样,它们不会在元素之间放置填充,因此它们是“内部连续的”。
但是,the std::array
object itself may be larger than the set of its elements!允许具有尾随的“填充”(例如填充)。因此,尽管可能,但在第一种情况下,数据全部不一定是连续的。
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
的向量将更清晰,完全打包,并且使用起来可能更安全。
对于向量的向量,向量实际上只是一个指针加上一些内部管理,因此是间接的(如您所说)。
答案 1 :(得分:18)
std::vector
和std::array
之间的最大区别在于,std::vector
包含一个指向它包装的内存的指针,而std::array
本身包含了一个实际的数组。
这意味着向量的向量就像jagged array。
对于数组的向量,std::array
对象将连续放置,但要与向量对象分开。请注意,std::array
对象本身可能大于其包含的数组,如果是,则数据将不是连续的。
最后一位还表示std::array
的数组(纯C样式或std::array
)也可能不会连续保留数据。数组中的std::array
对象是连续的,但数据不是连续的。
保证“多维”数组连续数据的唯一方法是嵌套的普通C样式数组。
答案 2 :(得分:11)
C ++标准不保证std::array
在数组的末尾不包含任何有效载荷,因此cannot,您不能假定后续数组的第一个元素紧随前一个数组的最后一个元素之后数组。
即使是这种情况,通过指向另一个数组中元素的指针的指针算术尝试到达数组中任何元素的行为也是不确定的。这是因为指针算术仅在数组内有效。
以上内容也适用于std::array<std::array>
。
答案 3 :(得分:6)
static_assert(sizeof(std::array<int,5>)==5*sizeof(int));
以上内容可避免在std::array
的末尾添加任何填充。到目前为止,没有任何主要的编译器会导致以上操作失败,我敢打赌将来不会。
当且仅当以上操作失败时,std::vector<std::array<int,5>> v(2)
之间的std::array
之间才会有一个“间隙”。
这并没有您想要的那么多;指针生成如下:
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
可以尝试修改b.x
,但是这样做通过这种方式是不确定的行为。
相同的限制条件使您无法从std::vector
中的第一个数组移到第二个数组(即,超出ptr+4
的数组)。
创建ptr+5
是合法的,但只能作为一种最后的指针。即使ptr+5 == &v[1][0]
的二进制值在每个主要硬件系统上的每个编译器中绝对相同,也不会在结果中指定比较std::vector<int>
。
如果您想进一步深入研究,由于指针别名的这些限制,甚至不可能在C ++本身中实现std::vector<int>
。最后我检查了(在c++17之前,但是我没有在C ++ 17中看到解决方案)标准委员会正在努力解决这一问题,但是我不知道这种努力的状态。 (这没有您想像的那样麻烦,因为没有要求在符合标准的C ++中实现watch
;它必须简单地具有标准定义的行为。它可以在内部使用特定于编译器的扩展。)>