这是我的编码:
vector<int>a;
a.reserve(14);
for (int i = 0; i < 10; i++) a.push_back(i);
a.end()++;
我分配了14个内存,但是我在这个向量中只使用了10个内存,所以我可以使用4个内存,但是当我使尾指针移动一位指向下一个内存时,编译器通知我尾指针指向一个未定义的空间,为什么?有四种记忆未被使用,我不明白。
以下是循环的操作:
a.end() --v v-- a.end()++
-------------------------------------------------------
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | | | | |
-------------------------------------------------------
我只是认为a.end()++指向a.end()之后存在的第12个内存。但为什么我不能制作一个指向那个空间的指针?
答案 0 :(得分:4)
有两件事需要考虑:
<强> 1 强> 想想end()正在返回的内容:
返回一个迭代器,引用向量容器中的past-the-end元素。
过去结束元素是跟随向量中最后一个元素的理论元素。它没有指向任何元素,因此不应被解除引用。 所以end()返回一个 not 的元素,它对于在循环条件中检查迭代器很有用:
iter != vec.end()
。
<强> 2 强> 再次误解了STL容器的大小和容量。
size(get by size()
)是存储在容器中的实际元素数。不同编译器之间的大小可能不同。在您的示例中,其中包含pushed back 10个元素。
capacity(get by capacity()
)是您的容器在引擎盖下使用的内存。这意味着在您的示例中,您reserved 14个元素作为容量。您可以将14个元素放入向量中,而无需重新分配。
简而言之:循环后,矢量大小为10,容量为14. end()
返回的迭代器指向不存在的11.元素。如果增加,则指向即使不存在的12.元素。当涉及到越界访问时,向量的at()方法得到了很好的解释:
该函数自动检查n是否在vector的有效元素范围内,如果不是{(3)}则抛出out_of_range异常(即,如果n大于或等于,则size)。
这意味着您只需访问:0 <= n < size()
。
还要考虑有一个resize()函数,它调整向量的大小,使其包含给定数量的元素。如果您使用resize()代替reserve(),则必须使用[] operator或at() method,因为如果您push_back()有更多元素,那么它将会落后于reserve()实际大小。
越界访问:
如果你想访问{{3}}在大小之后分配的内存,你可以vec[vec.size() + n] ... // access invalid!
(实际上你永远不应该这样做!)。如果没有编译器给你一个警告,这是可能的,因为编译器可以不检查向量的大小。
程序甚至不会在运行时崩溃,因为向量的内存保证是连续的。所以正如你所说的那样,内存就在大小之后。但是你通常不关心矢量的分配方式以及它背后的大小。 vector类是一个安全的抽象层,位于内存之上。
答案 1 :(得分:2)
我看不到保证在位置size() < position < capacity()
访问矢量数据的方法,即使有人认为底层内存是为capacity()
个元素保留的。原因是类vector
的标准仅在0..size()-1
范围内定义/保证访问,并且实现不必须保证访问超出该范围。
以函数T* vector::data()为例,其中......
返回指向用作元素存储的基础数组的指针。 指针是范围[data(); data()+ size())总是一个 有效范围,即使容器为空(data()不是 在那种情况下可以解除引用)
即使您获得了指向底层内存结构的指针,该标准也不保证在size()
- 位置之后访问。
因此即使像int x = v.data[v.size()]
这样的访问(假设仍然在容量范围内)可能不会崩溃,这样的访问依赖于库实现细节,因此肯定不可移植并且可能引入未定义的行为