Szabolcs
Szabolcs

Reputation: 25703

What does vec.data() return if vec.size() == 0?

cppreference has this note for std::vector::data:

Returns pointer to the underlying array serving as element storage. The pointer is such that range [data(); data() + size()) is always a valid range, even if the container is empty.

What does "valid range" mean here exactly? What will data() return if the vector is zero-length?

Specifically, for a zero-length vector:

  1. Can data() ever be a null pointer?
  2. Can it be safely dereferenced? (Even if it points to junk.)
  3. Is it guaranteed to be different between two different (zero-length) vectors?

I am working with a C library that takes arrays and won't allow a null pointer even for a zero-length array. However, it does not actually dereference the array storage pointer if the array length is zero, it just checks whether it is NULL. I want to make sure that I can safely pass data() to this C library, so the only relevant question is (1) above. (2) and (3) are just out of curiosity in case a similar situation comes up.


Update

Based on comments that were not turned into answers, we can try the following program:

#include <iostream>
#include <vector>

using namespace std;

int main() {
    vector<int> v;
    cout << v.data() << endl;

    v.push_back(1);
    cout << v.data() << endl;

    v.pop_back();
    cout << v.data() << endl;

    v.shrink_to_fit();
    cout << v.data() << endl;

    return 0;
}

With my compiler it output:

0x0
0x7f896b403300
0x7f896b403300
0x0

This shows that:

Yes, obviously I should have tried this before asking.

Upvotes: 29

Views: 3498

Answers (3)

Captain Giraffe
Captain Giraffe

Reputation: 14705

Too long for a comment so posting here.

I expected the iterators to be nullptr for an empty sequence so I tested it.

#include <iostream>
#include <vector>

void pr(std::vector<int>& v){
    std::cout << &*v.begin() << ", " << &*v.end() << "\n";
} 
// technically UB, but for this experiment I don't feel too bad about it.
// Thanks @Revolver    
int main(int argc, char** argv) {
    std::vector<int> v1;
    std::vector<int> v2;

    pr(v1);
    pr(v2);

    return 0;
}

And this does indeed print

0, 0
0, 0 

Now for an empty container the only reasonable operation for a valid range is begin() == end(). And no, junk can't be dereferenced so *v.begin() is not a concern.

Upvotes: 6

M.M
M.M

Reputation: 141638

"valid range" is defined by [iterator.requirements.general]/7 (C++14):

"Range [i,j) is valid if and only if j is reachable from i".

Luckily C++ defines that adding 0 to a null pointer yields a null pointer. So, is a null pointer reachable from a null pointer ? This is defined by point 6 of the same section:

An iterator j is called reachable from an iterator i if and only if there is a finite sequence of applications of the expression ++i that makes i == j.

A zero-length sequence is a finite sequence, therefore data() may return a null pointer.

Accordingly the answers to your questions are:

  1. Can data() ever be a null pointer?

Yes

  1. Can it be safely dereferenced? (Even if it points to junk.)

No

  1. Is it guaranteed to be different between two different (zero-length) vectors?

No

Upvotes: 22

Paul Evans
Paul Evans

Reputation: 27577

From the standard:

23.3.6.4 [vector.data]

T* data() noexcept;

const T* data() const noexcept;

Returns: A pointer such that [data(),data() + size()) is a valid range. For a non-empty vector, data() == &front().

So it's allowed to be null for an empty vector but not necessarily dereferencable nor unique.

Upvotes: 5

Related Questions