数组向量的内存布局是什么?

Con*_*nst 46 c++ stdvector language-lawyer c++11 stdarray

任何人都可以解释一下记忆的布局

std::vector<std::array<int, 5>> vec(2)
Run Code Online (Sandbox Code Playgroud)

它是否提供具有2行5个元素的2D数组的连续内存块?

据我所知,矢量矢量

std::vector<std::vector<int>> vec(2, std::vector<int>(5))
Run Code Online (Sandbox Code Playgroud)

提供存储器中不同位置两个 长度为 5个元素连续数组的存储器布局.

对于数组的向量是否相同?

Lig*_*ica 58

数组没有任何间接,但只是"直接"存储它们的数据.也就是说,std::array<int, 5>字面上int连续五个s,平坦.并且,与向量一样,它们不会在元素之间添加填充,因此它们"内部连续".

然而,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....
+----+----+----+-----+        +----+----+----+----+----+----------------------------+----+----+----+----+----+----------->
                   |          ^
                   \-----------
Run Code Online (Sandbox Code Playgroud)

而且,即使它是由于别名规则,你是否能够使用单个int*导航所有10个数字可能是另一回事!

总而言之,10 int秒的向量将更清晰,更完整,并且可能更安全.

在向量向量的情况下,向量实际上只是一个指针加上一些内务处理,因此间接(如你所说).

  • 根据这里的答案,数据不必是连续的:[嵌套的std :: arrays中的数据是否保证是连续的?](/sf/ask/683386371/).关于这个话题有一些讨论.另一个讨论:[std :: array的std :: array是否有连续的内存?](/sf/ask/3298469611/)和[标准定义的std :: array的大小](https ://stackoverflow.com/q/19103244/580083). (10认同)
  • 注意`static_assert(sizeof(std :: array <int,t>)== sizeof(int)*5)`减轻任何填充(并传入支持`std :: array`的每个主要编译器的每个版本).它不会减少别名问题. (3认同)

Som*_*ude 18

std::vector和之间的最大区别std::array是它std::vector包含指向它包装的内存的指针,同时std::array包含实际的数组本身.

这意味着矢量矢量就像一个锯齿状阵列.

对于数组矢量,std::array对象将连续放置,但与矢量对象分开.请注意,std::array对象本身可能比它们包含的数组大,如果是,那么数据将不是连续的.

最后一位也意味着数组(普通C风格或者std::array)std::array也可能不会连续保持数据.std::array数组中的对象将是连续的,但不是数据.

保证"多维"数组的连续数据的唯一方法是嵌套的普通C风格数组.

  • _这也意味着数组向量类似于数组数组,因为数据都是连续的......我不敢不同意.请在LightnessRacesinOrbit的回答下看看我的评论. (5认同)
  • @DanielLangr感谢您提醒我.改述那一部分. (2认同)

Bat*_*eba 11

C++标准不保证std::array在数组末尾不包含任何有效负载,因此你不能假设后续数组的第一个元素就在前一个数组的最后一个元素之后.

即使是这种情况,尝试通过指针算法在指向不同数组中的元素的指针上到达数组中的任何元素的行为也是未定义的.这是因为指针算法仅在数组中有效.

以上也适用于std::array<std::array>.


Yak*_*ont 6

static_assert(sizeof(std::array<int,5>)==5*sizeof(int));
Run Code Online (Sandbox Code Playgroud)

以上缓解了在一个结尾处有任何填充std::array.没有主要的编译器会导致上述内容未能达到此日期,我打赌将来也不会.

当且仅当上述情况失败时,s std::vector<std::array<int,5>> v(2)之间才会有"差距" std::array.

这没有你想要的那么多; 生成的指针如下:

int* ptr = &v[0][0];
Run Code Online (Sandbox Code Playgroud)

只有一个有效域ptr+5,并且解除引用ptr+5是未定义的行为.

这是由于别名规则造成的; 即使您知道它在那里,也不允许"走"过一个对象的末尾,即使您知道它在那里,除非您首先往返某些类型(例如char*)允许较少限制的指针算法.

反过来,该规则允许编译器通过哪个指针来推断正在访问哪些数据,而不必证明任意指针算法可以让您到达外部对象.

所以:

struct bob {
  int x,y,z;
};

bob b {1,2,3};
int* py = &b.y;
Run Code Online (Sandbox Code Playgroud)

不管你做什么py作为int*,你不能合法修改xz用它.

*py = 77;
py[-1]=3;
std::cout << b.x;
Run Code Online (Sandbox Code Playgroud)

编译器可以优化std::cout线条以简单地打印1,因为py[-1]=3可能会尝试修改b.x,但通过这种方式这样做是未定义的行为.

同样的限制会阻止您从第一个阵列进入std::vector第二个阵列(即超越ptr+4).

创建ptr+5是合法的,但仅作为一个过去的指针.ptr+5 == &v[1][0]即使在每个主要硬件系统上的每个编译器中它们的二进制值绝对相同,也不会在结果中指定比较.

如果你想进一步深入兔子洞,std::vector<int>由于指针别名的这些限制,甚至不可能在C++本身内实现.最后我检查了(这是在之前,但我没有在C++ 17中看到解决方案)标准委员会正在努力解决这个问题,但我不知道任何此类努力的状态.(这比您想象的要少,因为没有什么要求std::vector<int>在符合标准的C++中实现;它必须只具有标准定义的行为.它可以在内部使用特定于编译器的扩展.)