Reputation: 25703
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:
data()
ever be a null pointer?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:
data()
can indeed be a null pointer, thus the answers are (1) yes (2) no (3) no
but it is not always a null pointer for a zero-size vector
Yes, obviously I should have tried this before asking.
Upvotes: 29
Views: 3498
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
Reputation: 141638
"valid range" is defined by [iterator.requirements.general]/7 (C++14):
"Range
[i,j)
is valid if and only ifj
is reachable fromi
".
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 iteratori
if and only if there is a finite sequence of applications of the expression++i
that makesi == j
.
A zero-length sequence is a finite sequence, therefore data()
may return a null pointer.
Accordingly the answers to your questions are:
- Can
data()
ever be a null pointer?
Yes
- Can it be safely dereferenced? (Even if it points to junk.)
No
- Is it guaranteed to be different between two different (zero-length) vectors?
No
Upvotes: 22
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