【问题标题】:c++ vector object address and data address confusionc++向量对象地址和数据地址混淆
【发布时间】:2018-07-26 17:09:43
【问题描述】:

我创建了一个向量 a 并尝试显示 &a&a[0]。地址非常不同。对象地址通常不是与第一个元素地址相同吗?对象内存不是连续的吗?我注意到 &a+1&a 之间的地址差异与整个向量元素的大小完全相同,在这种情况下为 12 个字节。在我看来,它就像是整个向量的副本。

#include <iostream>
#include <vector>

using namespace std;

int main(){

    vector<int> a ={3,5,8};
    cout<<"vector object address: "<<&a<<endl;
    cout<<"vector object address +1: "<<&a+1<<endl;
    cout<<"vector data[0] address: "<<&a[0]<<endl;
    cout<<"vector data[1] address: "<<&a[1]<<endl;


    return 0;

}

谁能向我解释一下矢量对象是如何工作的?下面是代码输出:

vector object address: 0034FEBC
vector object address +1: 0034FEC8
vector data[0] address: 007D8208
vector data[1] address: 007D820C

【问题讨论】:

  • 请阅读the tour 并阅读the help page。请将代码发布为代码,而不是链接或图像。欢迎来到 SO。
  • “不是对象地址通常与第一个元素地址相同吗?” - 不,不是标准容器(可能除了std::array)。
  • 要寻址std::vector 的底层连续数组,请使用std::vector::data() 函数。取std::vector 变量本身的地址是另一回事。 @Galik is right about that 提到 std::array 是个例外。
  • 这两个[] 地址在我看来是连续的。还记得sizeof(int)吗?
  • @fuhuan26 在这里提问之前最好先咨询documentationstd::vector&lt;int&gt; 不是 C 风格 int[] 原始数组的 POD 替代品。虽然它完全可以使用data() 函数和size() 进行转换。

标签: c++ pointers memory memory-management reference


【解决方案1】:

向量的工作方式由实现定义。但是,默认情况下,他们需要将其元素动态存储在免费商店中。这意味着当您创建 std::vector 时,它会分别为其成员获取内存。

可以这样实现:

template<typename T>
class vector
{
public:

    // The returned element will not have the same address as this
    T& operator[](std::size_t n) { return m_begin[n]; } 

private:
    T* m_begin; // start of internal array
    T* m_top;   // end of contained elements
    T* m_end;   // end of internal array
};

注意:上述模型中缺少关键部分

元素的地址直接存储在向量对象中,而不是元素本身。

【讨论】:

  • 重点是POD类型还是不是IMO。 std::array 果然如你所想的那样出类拔萃。
  • 当然,当然!我只是尝试了一种不同的解释方法(可能源于误解)。提出这样的困惑似乎有点自然(对行为的最低期望),因此这个问题还不错。
【解决方案2】:

您所看到的解释是 std::vector 不像原始 c 样式数组那样遵循 array decaying 的相同规则:

#include <iostream>
using std::cout;
using std::endl;

int main() {

    int a[] ={3,5,8};
    cout<<"raw array object address: "<<&a<<endl;
    cout<<"raw array object address +1: "<<&a+1<<endl; // adds sizeof(a)
    cout<<"raw array data[0] address: "<<&a[0]<<endl;  // same as a + 0
    cout<<"raw array data[1] address: "<<&a[1]<<endl;  // same as a + sizeof(int)
}

输出:

raw array object address: 0x7ffd02ca2eb4
raw array object address +1: 0x7ffd02ca2ec0
raw array data[0] address: 0x7ffd02ca2eb4
raw array data[1] address: 0x7ffd02ca2eb8

See live demo

谁能给我解释一下矢量对象是如何工作的?

std::vector 实例是非POD 变量,并且在本地存储中将拥有自己的地址。它只是包装了实际用于存储数据的底层内存地址。 具体是如何定义实现的,但是你可以认为至少有一个interned 指针,一个allocator concept 用于获取内存。 跟踪当前大小并管理复制到重新分配的内存。

std::vector::data() 函数专用于访问有保证的连续内存块。

您应该注意到,您通过上述data() 函数获得的指针并不稳定,并且可能会在std::vector 以某种方式被操纵时失效。


还值得一提的是,std::array 在此处与其他标准容器 不同:

#include <iostream>
#include <array>

using std::array;
using std::cout;
using std::endl;

int main() {

    array<int,3> a ={3,5,8};
    cout<<"std::array object address: "<<&a<<endl; // Note these addresses ...
    cout<<"std::array object address +1: "<<&a+1<<endl;
    cout<<"std::array data[0] address: "<<&a[0]<<endl; // are the same
    cout<<"std::array data[1] address: "<<&a[1]<<endl;
}

输出:

std::array object address: 0x7ffe72f1cf24
std::array object address +1: 0x7ffe72f1cf30
std::array data[0] address: 0x7ffe72f1cf24
std::array data[1] address: 0x7ffe72f1cf28

Live demo

【讨论】:

    【解决方案3】:

    向量由两部分组成。一个是对象本身,它是固定大小的。另一个是向量中包含的数据,它是可变的。向量对象的成员之一将是指向向量所包含数据的指针&amp;a 返回指向向量对象的指针,&amp;a[0] 返回指向与对象不在相同位置的数据的指针。

    &amp;a 为您提供指向向量对象的指针。当您执行&amp;a+1 时,您正在使用指针算法,这将为您提供指向向量数组中下一个向量的指针。由于您没有数组,&amp;a+1 返回一个无效指针,但 C++ 无法知道这一点。

    【讨论】:

    • 嗨,马克,谢谢你的解释。所以一旦定义了向量对象,&amp;a 的值就会被修复。但是,&amp;a[0] 的值可以改变,如果向量扩展很多并且原始地址不够大。我对吗?您提到向量对象的一个​​成员是指向向量包含的数据的指针。如何调用该指针?该指针的地址始终与&amp;a[0] 相同,对吗? @Mark Ransom
    • @fuhuan26 -- 注意std::vector::operator [ ] 实际上是一个函数调用 -- 它不是一个类似于数组的内置运算符。所以对于vector来说,[ ]可以做它需要做的任何事情。
    • @fuhuan26 vector 维护的指针是私有的,它可能直接指向数据但你不能确定,没关系,因为你可以不要访问它。 &amp;a[0] 是获取数据指针的正确方法。是的,它可以随着向量的capacity 的变化而变化——标准保证它不会改变。
    • @Mark Ransom,知道了。并且一旦定义了向量对象,它的地址&amp;a 就永远不会改变,即使“容量”发生了变化,对吧?
    • @fuhuan26 除非该向量是 另一个 向量的一部分!但是是的,局部变量a 的地址永远不会改变。
    猜你喜欢
    • 1970-01-01
    • 2015-07-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-05
    • 2013-09-19
    • 2012-01-11
    • 2012-12-24
    • 1970-01-01
    相关资源
    最近更新 更多