c++ 11: &v[0] on empty std::vector v

Viewed 176

If I have an std::vector v that can sometimes be empty, is it just pure luck with g++ that I can get the pointer to this not-existing data with &v[0] or will I get a seg fault with other compilers (my g++ is fine with the below and has never given a seg fault on actual runs for that).

std::vector<int> v = {}; std::cout << (&v[0] == NULL) << std::endl; // true with my g++

Thanks

1 Answers

Yes your code has undefined behaviour.

v[0] calls a function which returns int&, and there are no "null references", so &v[0] can never be NULL in the first place. This is why clang with optimisations makes your code print 0, and optimises the following to return false;:

bool is_pointer_to_first_null(std::vector<int>& v) {
    return &v[0] == nullptr;
}

(Which clang also warns you of)

It also breaks the contract for std::vector<T>::operator[](n), which is defined as equivalent to *(v.begin() + n), and since v.begin() + 0 == v.begin() == v.end() for an empty vector, this is not a dereferenceable iterator, and your standard library implementation is allowed to do anything.
For example, with _GLIBCXX_DEBUG defined, this prints Error: attempt to subscript container with out-of-bounds index 0, but container only holds 0 elements. with g++. (https://godbolt.org/z/anran5)

The fact that &v[0] == nullptr isn't optimised to false in gcc is a missed optimisation. Perhaps &v[0] is already optimised to v.data() first and the information that this is non-null is lost (or not used for whatever reason).
And without optimisations, gcc will gladly form null-references since references are implemented as pointers and &reference is a noop in assembly. This is true for most compilers on most architectures, so this is unlikely to segfault.


Perhaps you are looking for v.empty() (which could still have a non-null data pointer), v.data() == nullptr (When defined, v.data() == &v[0]), or v.capacity() == 0? There shouldn't be a good reason to check if the data pointer is null though.

Related