将迭代器转换为指针?

69

我有一个包含n个元素的std::vector。现在我需要传递指向最后n-1个元素的向量的指针给函数。

例如,我的vector<int> foo包含(5,2,6,87,251)。一个函数需要vector<int>*,我想给它传一个指向(2,6,87,251)的指针。

我可以安全地使用迭代器++foo.begin(),将其转换为指针并将其传递给函数吗?或者使用&foo[1]

更新: 人们建议我将我的函数更改为接受一个迭代器而不是指针。在我的情况下,这似乎不可能,因为我提到的函数是unordered_set<std::vector*>find函数。那么,在这种情况下,是将n-1个元素从foo复制到新向量中,并使用该向量的指针调用find是唯一的选择吗?非常低效!就像Shlemiel画家一样,特别是因为我必须查询许多子集:最后n-1,然后是n-2等元素,并查看它们是否在unordered_set中。


你能再解释一下你真正想做什么吗?我有点困惑——看起来你正在尝试使用unordered_set<>和unordered_set<>::find()来做一些它们并不真正设计用于的事情(但我很可能只是理解有误)。 - Michael Burr
你为什么要使用向量来表示无序集合呢?如果你需要进行多个成员查询,那么有更高效的实现方式。你可以创建一个集合,然后对其进行查询。 - Uri
我有一组已知的序列S。 对于每个新序列,我必须快速找出它是否在S中。 我意识到将已知序列存储为单独的向量,并在unordered_set中存储指向它们的指针是浪费内存的,而使用trie会更好。 但是我想要常数时间的查找功能。 - Frank
一个类似的情况是一组字符串指针。每个字符串实际上都是字符向量。现在你有一个新的字符串 s,并想用指向 s.substr(0),s.substr(1)等的指针重复调用 unordered_set<>::find()。 - Frank
那么你真的有一个向量,还是你有一个集合,想要像处理向量一样处理它? - John Dibling
这是一个向量;每个向量存储像(1,1,2,5,2)这样的序列,即它可能有重复元素,因此它不是一个集合。我的无序集合然后存储指向这样的向量的指针。 - Frank
13个回答

128

这里有一个获取迭代器对应指针引用的示例:

string my_str= "hello world";

string::iterator it(my_str.begin());

char* pointer_inside_buffer=&(*it); //<--

[注意运算符 * 返回一个引用,因此在引用上执行 & 将给出地址。]


19
当迭代器指向my_str.end()时会发生什么?指针会指向字符串中的无效地址吗? - Unglued
13
如果你尝试引用 my_str.end(),调试版本会抛出一个断言 "string iterator not dereferencable",而发布版本会抛出一个异常。 - ahmd0
1
你可以通过 &my_str.front() + my_str.size() 获取结束指针的地址。 - Jcsq6

7
在我的情况下似乎不可能,因为我提到的函数是unordered_set<std::vector*>的查找函数。
你是否使用自定义哈希/谓词函数对象?如果没有,则必须将要查找的确切向量的指针传递给unordered_set<std::vector<int>*>::find()。具有相同内容的另一个向量的指针将无法工作。这对于查找来说并不是非常有用。
使用unordered_set<std::vector<int> >会更好,因为这样您可以通过值执行查找。我认为这也需要自定义哈希函数对象,因为hash没有我所知道的vector<int>的专门化。
无论哪种方式,一个指向向量中间的指针本身不是向量,正如其他人已经解释过的那样。您不能将迭代器转换为指向向量的指针而不复制其内容。

快速回答你的问题:是的,我正在使用一个自定义谓词对象,通过比较它们的元素来决定两个向量是否相同。(嗯,那个查找并不完全是常数时间...) - Frank

6
如果可以的话,更好的选择可能是将函数改为接受元素的迭代器或全新的向量(如果不进行修改)。
虽然您可以使用数组来做这种事情,因为您知道它们是如何存储的,但是在向量中使用相同的方法可能不是一个好主意。&foo [1] 的类型不是 vector<int>*。
此外,尽管STL实现可以在线获得,但是尝试依赖抽象的内部结构通常是有风险的。

4

你的函数不应该接受vector<int>*,而应该根据情况接受vector<int>::iteratorvector<int>::const_iterator。然后,只需传入foo.begin() + 1


3

向量是一种具有完全拥有其元素的容器。一个向量不能持有另一个部分视图,即使是一个const视图。这就是根本原因。

如果您需要这个功能,请创建自己的容器,其中包含对数据的弱指针视图,或查看范围。迭代器对(甚至指针也可以作为向量的迭代器)或者更好的是boost::iterator_range,它们可以很好地无缝工作。

这取决于您的代码的模板化程度。如果您需要将代码隐藏在cpp中,请使用std::pair。


2
例如,我的vector<int> foo包含(5,2,6,87,251)。一个函数需要vector<int>*,我想传递指向(2,6,87,251)的指针。
指向vector<int>的指针与指向向量元素的指针完全不同。
为了做到这一点,您需要创建一个新的vector<int>,其中只有您想要传递指针的元素。类似于:
 vector<int> tempVector( foo.begin()+1, foo.end());

 // now you can pass &tempVector to your function

然而,如果您的函数接受一个指向 int 数组的指针,则可以传递 &foo[1]


2

直接回答你的问题是肯定的。如果foo是一个向量,你可以这样做:&foo[1]。

然而,这仅适用于向量,因为标准规定向量使用连续内存实现存储。

但您仍然可以(并且可能应该)传递迭代器而不是裸指针,因为这更具表现力。传递迭代器不会复制向量。


1
我认为&foo[1]将是一个整数数组,而不是vector<int>。vector<int>知道它的长度。&foo[1]是否知道它的长度? - Thomas L Holaday
&foo[1] 不知道它的长度,是的,&foo[1] 将是一个整数数组。 - John Dibling

1

使用vector::front,这应该是最便携的解决方案。当我与需要char指针的固定API进行接口时,我使用了这个方法。例如:

void funcThatTakesCharPtr(char* start, size_t size);

...

void myFunc(vector<char>& myVec)
{
    // Get a pointer to the front element of my vector:
    char* myDataPtr = &(myVec.front());

    // Pass that pointer to my external API:
    funcThatTakesCharPtr(myDataPtr, myVec.size());
}

0
一个将迭代器安全转换为指针的版本(无论意味着什么,都没有担心必须解引用迭代器并由于 `end()` / 其他情况导致可能的异常/错误而产生安全问题)
#include <iostream>
#include <vector>
#include <string.h>

int main()
{
    std::vector<int> vec;

    char itPtr[25];
    long long itPtrDec;
    
    std::vector<int>::iterator it = vec.begin();
    memset(&itPtr, 0, 25);
    sprintf(itPtr, "%llu", it);
    itPtrDec = atoll(itPtr);
    printf("it = 0x%X\n", itPtrDec);
    
    vec.push_back(123);
    it = vec.begin();
    memset(&itPtr, 0, 25);
    sprintf(itPtr, "%llu", it);
    itPtrDec = atoll(itPtr);
    printf("it = 0x%X\n", itPtrDec);
}

将会打印类似以下的内容:

it = 0x0

it = 0x2202E10

这是一种非常不正规的方法,但如果有需要的话,它可以完成工作。你会收到一些编译器警告,如果真的很烦人,可以用#pragma去除。

0
如果您的函数确实接受vector<int>*(指向向量的指针),则应传递&foo,因为那将是指向向量的指针。显然,这并不能简单地解决您的问题,因为您不能直接将迭代器转换为向量,因为迭代器地址处的内存不会直接寻址有效的向量。
您可以通过调用vector构造函数来构造一个新向量:
template <class InputIterator> vector(InputIterator, InputIterator)

这将通过复制两个迭代器之间的元素来构造一个新的向量。您可以大致按照以下方式使用它:

bar(std::vector<int>(foo.begin()+1, foo.end());

但是这将传递一个指向内容为(5,2,6,87,251)的向量的指针,而我想传递一个指向内容为(2,6,87,251)的向量的指针。 - Frank
是的,我误读了问题。我更新了一些新细节,提供了一个可能的解决方案。 - 1800 INFORMATION

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接