66

I have a std::vector with n elements. Now I need to pass a pointer to a vector that has the last n-1 elements to a function.

For example, my vector<int> foo contains (5,2,6,87,251). A function takes vector<int>* and I want to pass it a pointer to (2,6,87,251).

Can I just (safely) take the iterator ++foo.begin(), convert it to a pointer and pass that to the function? Or use &foo[1]?

UPDATE: People suggest that I change my function to take an iterator rather than a pointer. That seems not possible in my situation, since the function I mentioned is the find function of unordered_set<std::vector*>. So in that case, is copying the n-1 elements from foo into a new vector and calling find with a pointer to that the only option? Very inefficient! It's like Shlemiel the painter, especially since i have to query many subsets: the last n-1, then n-2, etc. elements and see if they are in the unordered_set.

lsalamon
  • 7,998
  • 6
  • 50
  • 63
Frank
  • 64,140
  • 93
  • 237
  • 324
  • Could you explain a little bit more what you're really trying to do? I'm a little confused - it seems like you're trying to do something with unordered_set<> and unordered_set<>::find() that they aren't really designed to do (but I'm quite possibly just not understanding). – Michael Burr Apr 13 '09 at 06:33
  • Why are you using a vector to represent an unordered set in the first place? If you're doing multiple membership queries, there are much more efficient implementations. You may want to create a set and then query against it. – Uri Apr 13 '09 at 06:51
  • I have a set S of known sequences. For each new sequence, I have to find out quickly if it's in S or not. I realize that storing the known sequences as separate vectors and storing pointers to them in unordered_set is a memory waste, and a trie would be better. But I wanted the constant-time lookup. – Frank Apr 13 '09 at 11:55
  • A similar case would be a set of string pointers. Each string is really a vector of chars. Now you have a new string s and want to call unordered_set<>::find() repeatedly with a pointer to s.substr(0), s.substr(1), ... – Frank Apr 13 '09 at 12:04
  • So do you really have a vector, or do you have a set that you are trying to treat like a vector? – John Dibling Apr 13 '09 at 14:22
  • It's a vector; each vector stores a sequence like (1,1,2,5,2), i.e. it may have repeated elements, so it's not a set. My unordered_set then stores pointers to such vectors. – Frank Apr 13 '09 at 14:34

13 Answers13

125

here it is, obtaining a reference to the coresponding pointer of an iterator use :

example:

string my_str= "hello world";

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

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

[notice operator * returns a reference so doing & on a reference will give you the address].

luiscubal
  • 24,773
  • 9
  • 57
  • 83
Robocide
  • 6,353
  • 4
  • 37
  • 41
  • 19
    what happens when the iterator points to ```my_str.end()```? Will the pointer point to an invalid address in the string? – Unglued Jun 22 '14 at 02:03
  • 12
    @Unglued: If you try to reference `my_str.end()` a debugger version will throw an assertion `"string iterator not dereferencable"` and the release version will throw an exception. – ahmd0 Apr 08 '16 at 23:08
  • 1
    You can obtain the address of the end pointer via `&my_str.front() + my_str.size()` – Jcsq6 Jan 29 '22 at 02:22
7

That seems not possible in my situation, since the function I mentioned is the find function of unordered_set<std::vector*>.

Are you using custom hash/predicate function objects? If not, then you must pass unordered_set<std::vector<int>*>::find() the pointer to the exact vector that you want to find. A pointer to another vector with the same contents will not work. This is not very useful for lookups, to say the least.

Using unordered_set<std::vector<int> > would be better, because then you could perform lookups by value. I think that would also require a custom hash function object because hash does not to my knowledge have a specialization for vector<int>.

Either way, a pointer into the middle of a vector is not itself a vector, as others have explained. You cannot convert an iterator into a pointer to vector without copying its contents.

bk1e
  • 23,871
  • 6
  • 54
  • 65
  • Quick answer to your question: Yes, I am using a custom predicate object that decides if two vectors are the same by comparing their elements. (Hmm, that lookup isn't exactly constant-time then ...) – Frank Apr 13 '09 at 12:24
6

If you can, a better choice may be to change the function to take either an iterator to an element or a brand new vector (if it does not modify).

While you can do this sort of things with arrays since you know how they are stored, it's probably a bad idea to do the same with vectors. &foo[1] does not have the type vector<int>*.

Also, while the STL implementation is available online, it's usually risky to try and rely on the internal structure of an abstraction.

Uri
  • 88,451
  • 51
  • 221
  • 321
4

Your function shouldn't take vector<int>*; it should take vector<int>::iterator or vector<int>::const_iterator as appropriate. Then, just pass in foo.begin() + 1.

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
3

A vector is a container with full ownership of it's elements. One vector cannot hold a partial view of another, even a const-view. That's the root cause here.

If you need that, make your own container that has views with weak_ptr's to the data, or look at ranges. Pair of iterators (even pointers work well as iterators into a vector) or, even better, boost::iterator_range that work pretty seamlessly.

It depends on the templatability of your code. Use std::pair if you need to hide the code in a cpp.

Macke
  • 24,812
  • 7
  • 82
  • 118
2

For example, my vector<int> foo contains (5,2,6,87,251). A function takes vector<int>* and I want to pass it a pointer to (2,6,87,251).

A pointer to a vector<int> is not at all the same thing as a pointer to the elements of the vector.

In order to do this you will need to create a new vector<int> with just the elements you want in it to pass a pointer to. Something like:

 vector<int> tempVector( foo.begin()+1, foo.end());

 // now you can pass &tempVector to your function

However, if your function takes a pointer to an array of int, then you can pass &foo[1].

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
2

The direct answer to your question is yes. If foo is a vector, you can do this: &foo[1].

This only works for vectors however, because the standard says that vectors implement storage by using contigious memory.

But you still can (and probably should) pass iterators instead of raw pointers because it is more expressive. Passing iterators does not make a copy of the vector.

John Dibling
  • 99,718
  • 31
  • 186
  • 324
1

Use vector::front, it should be the most portable solution. I've used this when I'm interfacing with a fixed API that wants a char ptr. Example:

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());
}
Ogre Psalm33
  • 21,366
  • 16
  • 74
  • 92
0

A safe version to convert an iterator to a pointer (exactly what that means regardless of the implications) and by safe I mean no worries about having to dereference the iterator and cause possible exceptions / errors due to end() / other situations

#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);
}

will print something like

it = 0x0

it = 0x2202E10

It's an incredibly hacky way to do it, but if you need it, it does the job. You will receive some compiler warnings which, if really bothering you, can be removed with #pragma

Community
  • 1
  • 1
QuantumBlack
  • 1,549
  • 11
  • 27
0

If your function really takes vector<int> * (a pointer to vector), then you should pass &foo since that will be a pointer to the vector. Obviously that will not simply solve your problem, but you cannot directly convert an iterator to a vector, since the memory at the address of the iterator will not directly address a valid vector.

You can construct a new vector by calling the vector constructor:

template <class InputIterator> vector(InputIterator, InputIterator)

This constructs a new vector by copying the elements between the two iterators. You would use it roughly like this:

bar(std::vector<int>(foo.begin()+1, foo.end());
1800 INFORMATION
  • 131,367
  • 29
  • 160
  • 239
  • But that will pass it a pointer to the vector with content (5,2,6,87,251), whereas I want to pass it a pointer to a vector with content (2,6,87,251). – Frank Apr 13 '09 at 04:45
  • Yeah I had misread the question. I've updated with some new detail for one possible solution – 1800 INFORMATION Apr 13 '09 at 04:53
0

I haven't tested this but could you use a set of pairs of iterators instead? Each iterator pair would represent the begin and end iterator of the sequence vector. E.g.:

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 ();
}

Obviously the vectors have to be held elsewhere as before. Also if a sequence vector is modified then its entry in the set would have to be removed and re-added as the iterators may have changed.

Jon

jon hanson
  • 8,722
  • 2
  • 37
  • 61
0

Vector is a template class and it is not safe to convert the contents of a class to a pointer : You cannot inherit the vector class to add this new functionality. and changing the function parameter is actually a better idea. Jst create another vector of int vector temp_foo (foo.begin[X],foo.end()); and pass this vector to you functions

BenMorel
  • 34,448
  • 50
  • 182
  • 322
-1
std::vector<int> v;
  :
auto it=v.end();
auto ptr=v.data()+std::distance(v.begin(),it);
  • 1
    This does not answer the original question. Also, the resulting pointer points past the container. – zkoza Apr 04 '21 at 10:38
  • auto it = v.end (); is just one example, and the code that follows can safely convert an iterator to a pointer. end () should go beyond the scope of the container. You can still safely convert it to a pointer. – Hideaki Kazaoka Apr 11 '21 at 07:07