Search code examples
c++memoryvector

CAN access elements of 0-length vector. Why?


I don't understand why the C++ code below works. In this program the user specifies size of array and the array itself, then the program prints length of the array and it's elements. I use reserve() to allocate memory for the array elements and as far as I know I have to use push_back() to add elements to vector, but I don't do that and though it always prints length 0, it accesses vector elements without any issues and prints them correctly. Why is this the case?

#include <iostream>
#include <vector>

using namespace std;

int main(){
    unsigned size;
    cin >> size;
    
    vector<unsigned long> nums;
    nums.reserve(size);
    for(unsigned i{0}; i < size; ++i) cin >> nums[i];
    
    cout << "length " << nums.size() << '\n';
    
    for(unsigned i{0}; i < size; ++i) cout << i << ' ' << nums[i] << endl;
    
    return 0;
}
Example
Input
3     
45 12 1454
output
length 0
0 45
1 12
2 1454

Try it yourself


Solution

  • What you doing is undefined behaviour so there are absolutely no guarantees on how your program will behave.

    That said, we can follow reasonable std::vector implementation and see why it should work.

    Underneath, the vector is just a block of heap allocated memory similar to new unsigned char[N] (discarding alignment issues) and few pointers/offsets. One offset points to the true end of allocated block - the reserved size of the vector. The other points to the end of last element - the current size of the vector.

    So, reserve() is what requests enough memory from the OS, resize() is what fills the memory with stored objects and if there is not enough reserved, it also reserves it.

    operator[] is dumb and therefore fast, it just takes the pointer to the block, adds the offset and dereferences that pointer. It does not care whether this virtual memory address belongs to a page which has been given by OS to our process and even if so, whether it belongs to this particular vector.

    But this is the reason why it works, you reserved this memory, so it belongs to your process and therefore you do not get any segmentation faults - complaints from OS. There is not much memory protection on finer scale than the process level unless you enable it so e.g. by using memory sanitizers. What you are doing though is bypassing the logic of the vector, no wonder it becomes "out of sync" - if you store there any objects, later push_back will happily overwrite them because as far as the vector knows, there are no objects because it did not put any there. That is also why the size is zero, of course it is, how could it be anything else? Did you increment the internal offset of the vector? No, you wrongly bypassed all of that - the vector did not store any elements, you did and in the memory that does not belong to you.