在填充std::矢量时使用不同的地址

Different addresses while filling a std::vector

本文关键字:地址 填充 std      更新时间:2023-10-16

难道不希望两个循环打印的地址相同吗?我是,我不明白为什么(有时)它们不同。

#include <iostream>
#include <vector>
using namespace std;
struct S {
  void print_address() {
    cout << this << endl;
  }
};
int main(int argc,char *argv[]) {
  vector<S> v;
  for (size_t i = 0; i < 10; i++) {
    v.push_back( S() );
    v.back().print_address();
  }
  cout << endl;
  for (size_t i = 0; i < v.size(); i++) {
    v[i].print_address();
  } 
  return 0;
}

我用许多本地和在线编译器测试了这段代码,得到的输出如下(最后三个数字总是一样的):

0xaec010
0xaec031
0xaec012
0xaec013
0xaec034
0xaec035
0xaec036
0xaec037
0xaec018
0xaec019
0xaec010
0xaec011
0xaec012
0xaec013
0xaec014
0xaec015
0xaec016
0xaec017
0xaec018
0xaec019

我发现这一点是因为在第一个循环中进行一些初始化时,我在程序的后续部分获得了未初始化的对象。我是不是错过了什么?

因为当向量capicity改变时,它会重新分配元素。若您std::vector::保留足够的容量,不需要重新分配,它将打印相同的地址。

vector<S> v;
v.reserve(10);

注意:正确使用std::vector::reserve将提高应用程序性能,因为没有不必要的重新分配和对象复制。

矢量正在执行重新分配,以便根据需要增长。每次这样做时,它都会为数据分配一个更大的缓冲区,并跨其复制元素。您可以在第一个循环中清楚地看到这一点,其中每个地址跳跃后面都有一个更大的连续地址序列。在第二个循环中,您只需查看最终重新分配后的地址。

0xaec010
0xaec031  <--
0xaec012  <--
0xaec013
0xaec034  <--
0xaec035
0xaec036
0xaec037
0xaec018  <--
0xaec019

实例化具有10个S对象的矢量的最简单方法是

std::vector<S> v(10);

这将不涉及重新分配。另请参见std::vector::reserve

矢量元素被连续存储;也就是说,它们在记忆中都排成一行。矢量对象必须为这个连续的元素块分配空间。

你的向量不能无限期地添加东西。它必须扩大分配的空间。内存模型通常不允许我们扩展内存块—我们必须创建一个新的。当向量执行此操作时,它必须将其所有元素移动到新空间。这在你的第一个循环中发生了好几次。

如果你做了:

vector<S> v;
v.reserve(10);

(你可以这样做,因为你知道你最终会得到10个元素),那么就不需要重新分配,地址也不会改变。

我对它们的变化并不感到惊讶。由于向量最初没有大小,因此可能会在初始循环期间重新分配向量一到两次。这将改变向量的基地址。调整大小后,你最终会使用以前使用过的地址,这并非不可能(尽管我觉得这有点令人惊讶。你确定地址的第一部分吗?)

如果你想确保它们不会改变,你需要在开始推送内容之前添加一个v.reserve()