【问题标题】:Convert iterator to pointer?将迭代器转换为指针?
【发布时间】:2010-10-19 02:06:28
【问题描述】:

我有一个带有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() 做一些它们并不是真正设计用来做的事情(但我很可能只是不理解) .
  • 你为什么首先使用向量来表示无序集?如果您正在执行多个成员资格查询,则有更有效的实现。您可能想要创建一个集合,然后对其进行查询。
  • 我有一组已知序列。对于每个新序列,我必须快速找出它是否在 S 中。我意识到将已知序列存储为单独的向量并将指向它们的指针存储在 unordered_set 中是一种内存浪费,而 trie 会更好。但我想要恒定时间查找。
  • 类似的情况是一组字符串指针。每个字符串实际上是一个字符向量。现在你有一个新的字符串 s 并且想用指向 s.substr(0), s.substr(1), ... 的指针重复调用 unordered_set::find()
  • 那么你真的有一个向量,或者你有一个你想像向量一样对待的集合吗?

标签: c++ stl pointers vector iterator


【解决方案1】:

在这里,获取对迭代器使用的对应指针的引用:

示例:

string my_str= "hello world";

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

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

[notice operator * 返回一个reference,所以在引用上执行 & 会给你地址]。

【讨论】:

  • 当迭代器指向my_str.end()时会发生什么?指针会指向字符串中的无效地址吗?
  • @Unglued:如果您尝试引用my_str.end(),调试器版本将抛出断言"string iterator not dereferencable",而发布版本将抛出异常。
  • 可以通过&amp;my_str.front() + my_str.size()获取结束指针的地址
【解决方案2】:

在我的情况下这似乎是不可能的,因为我提到的函数是 unordered_set&lt;std::vector*&gt; 的查找函数。

您是否使用自定义哈希/谓词函数对象?如果没有,那么您必须将 unordered_set&lt;std::vector&lt;int&gt;*&gt;::find() 指针传递给您要查找的确切向量。指向具有相同内容的另一个向量的指针将不起作用。至少可以说,这对于查找不是很有用。

使用unordered_set&lt;std::vector&lt;int&gt; &gt; 会更好,因为这样您就可以按值执行查找。我认为这还需要一个自定义哈希函数对象,因为据我所知hash 没有专门针对vector&lt;int&gt;

无论哪种方式,指向向量中间的指针本身都不是向量,正如其他人所解释的那样。如果不复制其内容,则无法将迭代器转换为指向向量的指针。

【讨论】:

  • 快速回答您的问题:是的,我正在使用自定义谓词对象,通过比较它们的元素来确定两个向量是否相同。 (嗯,那个查找并不完全是恒定时间......)
【解决方案3】:

如果可以,更好的选择可能是更改函数以将迭代器带到元素或全新的向量(如果它不修改)。

虽然您可以对数组执行此类操作,因为您知道它们是如何存储的,但对向量执行相同操作可能不是一个好主意。 &amp;foo[1] 没有 vector&lt;int&gt;* 类型。

此外,虽然 STL 实现可以在线获得,但尝试依赖抽象的内部结构通常是有风险的。

【讨论】:

    【解决方案4】:

    你的函数不应该使用vector&lt;int&gt;*;它应该酌情采用vector&lt;int&gt;::iteratorvector&lt;int&gt;::const_iterator。然后,只需传入foo.begin() + 1

    【讨论】:

      【解决方案5】:

      向量是对其元素拥有完全所有权的容器。一个向量不能包含另一个向量的部分视图,即使是 const 视图。这就是这里的根本原因。

      如果您需要,请创建您自己的容器,该容器具有带有weak_ptr 的数据视图,或查看范围。一对迭代器(即使是指针也可以很好地作为向量中的迭代器),或者更好的是 boost::iterator_range,它们可以非常无缝地工作。

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

      【讨论】:

        【解决方案6】:

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

        但这仅适用于向量,因为标准规定向量通过使用连续内存来实现存储。

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

        【讨论】:

        • 我认为 &foo[1] 将是一个整数数组,而不是一个向量。 vector 知道它的长度。 &foo[1] 知道它的长度吗?
        • &foo[1] 不知道它的长度,不。是的,&foo[1] 将是一个整数数组。
        【解决方案7】:

        例如,我的vector&lt;int&gt; foo 包含 (5,2,6,87,251)。一个函数接受vector&lt;int&gt;*,我想将一个指向(2,6,87,251)的指针传递给它。

        指向vector&lt;int&gt; 的指针与指向向量元素的指针完全不同。

        为此,您需要创建一个新的vector&lt;int&gt;,其中只包含您希望在其中传递指针的元素。比如:

         vector<int> tempVector( foo.begin()+1, foo.end());
        
         // now you can pass &tempVector to your function
        

        但是,如果您的函数需要一个指向 int 的 array 的指针,那么您可以传递 &amp;foo[1]

        【讨论】:

          【解决方案8】:

          使用vector::front,它应该是最便携的解决方案。我在与需要 char ptr 的固定 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());
          }
          

          【讨论】:

            【解决方案9】:

            如果你的函数真的需要vector&lt;int&gt; *(一个指向向量的指针),那么你应该传递&amp;foo,因为这将是一个指向向量的指针。显然这不会简单地解决您的问题,但您不能直接将迭代器转换为向量,因为迭代器地址处的内存不会直接寻址有效向量。

            你可以通过调用vector constructor来构造一个新的向量:

            template <class InputIterator> vector(InputIterator, InputIterator)
            

            这通过复制两个迭代器之间的元素来构造一个新向量。你大概会这样使用它:

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

            【讨论】:

            • 但这会将指针传递给具有内容 (5,2,6,87,251) 的向量,而我想将指针传递给具有内容 (2,6,87,251) 的向量.
            • 是的,我误读了这个问题。我已经为一种可能的解决方案更新了一些新的细节
            【解决方案10】:

            我没有对此进行测试,但您可以使用一组迭代器来代替吗?每个迭代器对将代表序列向量的开始和结束迭代器。例如:

            typedef std::vector<int> Seq;
            typedef std::pair<Seq::const_iterator, Seq::const_iterator> SeqRange;
            
            bool operator< (const SeqRange& lhs, const SeqRange& rhs)
            {
                Seq::const_iterator lhsNext = lhs.first;
                Seq::const_iterator rhsNext = rhs.first;
            
                while (lhsNext != lhs.second && rhsNext != rhs.second)
                    if (*lhsNext < *rhsNext)
                        return true;
                    else if (*lhsNext > *rhsNext)
                        return false;
            
                return false;
            }
            
            typedef std::set<SeqRange, std::less<SeqRange> > SeqSet;
            
            Seq sequences;
            
            void test (const SeqSet& seqSet, const SeqRange& seq)
            {
                bool find = seqSet.find (seq) != seqSet.end ();
                bool find2 = seqSet.find (SeqRange (seq.first + 1, seq.second)) != seqSet.end ();
            }
            

            很明显,向量必须像以前一样保存在其他地方。此外,如果修改了序列向量,则必须删除并重新添加其在集合中的条目,因为迭代器可能已更改。

            乔恩

            【讨论】:

              【解决方案11】:

              Vector 是一个模板类,将类的内容转换为指针是不安全的: 您不能继承向量类来添加此新功能。 并且更改函数参数实际上是一个更好的主意。 Jst创建另一个int向量 向量 temp_foo (foo.begin[X],foo.end()); 并将这个向量传递给你的函数

              【讨论】:

                【解决方案12】:

                将迭代器转换为指针的安全版本(无论含义如何,这意味着什么)并且安全的意思是不用担心必须取消引用迭代器并由于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);
                }
                

                会打印类似的东西

                它 = 0x0

                它 = 0x2202E10

                这是一种令人难以置信的 hacky 方式,但如果您需要它,它就可以完成工作。你会收到一些编译器警告,如果真的困扰你,可以用#pragma删除

                【讨论】:

                  【解决方案13】:
                  std::vector<int> v;
                    :
                  auto it=v.end();
                  auto ptr=v.data()+std::distance(v.begin(),it);
                  

                  【讨论】:

                  • 这没有回答原始问题。此外,生成的指针指向容器之外。
                  • auto it = v.end ();只是一个例子,下面的代码可以安全地将迭代器转换为指针。 end() 应该超出容器的范围。您仍然可以安全地将其转换为指针。
                  猜你喜欢
                  • 2023-04-04
                  • 1970-01-01
                  • 2016-09-17
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2021-07-12
                  • 2015-04-28
                  • 1970-01-01
                  相关资源
                  最近更新 更多