通过指针vs end()访问std::vector元素

Accessing std::vector elements via pointers vs end()

本文关键字:std 访问 vector 元素 指针 vs end      更新时间:2023-10-16

我需要能够通过指针访问a和std::vector的元素(只读,不涉及调整大小或类似的东西)。例如,

std::vector<int> foo(10);
int *ptr_begin = &foo[0];

到目前为止一切顺利,这保证在当前标准(23.3.6.1)中工作:

一个向量的元素是连续存储的,这意味着如果v是一个向量,而T不是bool类型,那么对于所有0 <= n int *ptr_end = ptr_begin + foo.size()

(注意,我并没有试图访问过最后一个值,只是定义一个指向它的指针——如果你愿意,它相当于foo.end())。标准中提到只能通过指针运算来访问元素,但显然这里我们并没有访问任何元素。

(作为旁注,过去的东西存在的定义似乎与数组的基本概念紧密相关(例如参见5.7/5),但在整个标准中,似乎连续存储和数组的概念可以互换使用。我看错了吗?

是的,只要您只在比较*中使用ptr_end,并且不试图遵守它,这是可以的。引用c++ 11标准草案第5.7节关于指针加性操作的内容:

如果指针操作数和结果都指向相同的数组对象或数组对象最后一个元素的后面一个,评估不应产生溢出;否则,行为是未定义的。

在§5.9中为关系操作符列出了类似的规定:

如果两个指针指向同一数组的元素,或者指向数组以外的元素数组的末尾,指向下标较高的对象的指针比较高。

关于vector的缓冲区是否算作一个数组,§8.3.4声明:

数组类型的对象包含连续分配的非空对象t型N个子对象的集合

这与§23.3.6.1关于vector所说的是一致的:

vector的元素连续存储


由于指针是iterator s,对于使用标准库算法将任意内存块作为输入来说,这是一个方便的技巧。例如,假设您想使用lower_bound算法,但您的数据存储在MFC数组中:

CArray<int> someInts;
// populate and check for empty
int* begin = someInts.GetData();
int* end = begin + someInts.GetSize(); // for bounds-checking only; don't dereference
int* answer = std::lower_bound(begin, end, 100);

*还有一些其他的操作也是合法的;例如,既然你知道你的向量不是空的,你可以减去1来获得指向最后一个元素的指针。重要的是不要取消引用

您应该记住std::vector::data()的存在,它显式地为您提供了指向该连续内存的指针。所以,是的,只读操作是可用的(如果你没有将它们与其他向量方法混合,这可能会在下面进行一些重新分配等)。

我甚至会更进一步-在std::vector::data()下更改内存内容也应该是合法的。

是的,它是合法的,形成的地址到one-past-the-end元素。违抗它是不合法的。标准草案甚至有一个脚注,指出"实现只需要在对象结束后提供一个额外的字节(可能与程序中的另一个对象重叠),以满足"最后一个元素"的要求"。你也可以用它来做指针数学运算(至少是所有你在处理指向数组的指针时可以做的合法的事情)。