[英]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.