Reputation: 6623
I was doing some experimentation with vectors in C++. The code I'm using is the following
#include <vector>
#include <iostream>
int main()
{
std::vector<float> aVector = std::vector<float>(10); // IMPORTANT LINE
std::cout << "Vector size: " << aVector.size() << std::endl;
aVector.clear();
aVector[0] = 2.2;
std::cout << "Vector size: " << aVector.size() << std::endl;
aVector.push_back(0.1);
std::cout << "Vector size: " << aVector.size() << std::endl;
aVector.clear();
std::cout << "Vector size: " << aVector.size() << std::endl;
aVector[0] = 2.1;
std::cout << "Vector size: " << aVector.size() << std::endl;
return 0;
}
If I have the vector, initialize it with N elements and then clear the vector and try to put assign value aVector[0] = 1.1
the program goes all the way to the end with no segmentation faults.
On the other hand if I create a vector but don't pass the number of initial elements, when trying to assign the value with aVector[0] = 1.1
it will crash the program.
The only way a segmentation fault can arise is because I'm trying to write to an invalid memory address. So, to my understanding, creating the vector and not initializing means that the pointer to the actual vector data points to nowhere while initializing the vector and clearing keeps a pointer to an allocated memory but the size of the vector only shrinks to 0? does it mean that even after clearing a vector the values previously stored are still there?
Is this the behavior defined in the specification? I would've expected that clear erases and frees the memory previously allocated.
Upvotes: 0
Views: 8431
Reputation: 19721
The memory allocated by the vector is not restricted to the memory used by the vector. Let's go through the program step by step. Note that I'll describe what actually happens; formally you invoke undefined behaviour when trying to write to aVector[0]
when aVector.size()==0
no matter whether you initially allocated any elements or not):
std::vector<float> aVector = std::vector<float>(10); // IMPORTANT LINE
Here you initialize the vector with having 10 elements default-initialized. That is, it allocates space for 10 floats, and initializes each one to zero. The memory now looks like this:
+-------+-----+-------------+
| begin | end | capacity=10 |
+-------+-----+-------------+
| |
| \----------------------------------------------------\
| |
V V
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
The capacity tells you how many cells have been allocated.
Note BTW that you could have just written std::vector<float> aVector(10);
Now you do
std::cout << "Vector size: " << aVector.size() << std::endl;
The function std::vector<>::size()
just calculates the difference between begin
and end
. Thus you get 10
, for the 10 elements between begin
and end
.
aVector.clear();
This resets end
to equal begin
and destructs all objects beyond that new end (which for float
usually means do nothing; however a debug version might e.g. overwrite the floats with NaN). That is, your vector now looks like this:
+-------+-----+-------------+
| begin | end | capacity=10 |
+-------+-----+-------------+
| |
|/-----/
|
V
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| ??? | ??? | ??? | ??? | ??? | ??? | ??? | ??? | ??? | ??? |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
You see, the memory is still there.
aVector[0] = 2.2;
Since std::vector<>::operator[]
does not do range checks (although debug versions might do them), this results in an assignment of the float pointed to by begin
. Since floats can be assigned to even if not currently initialized (things would be different if you had e.g. a std::vector<std::string>
), this assignment succeeds, and you now have
+-------+-----+-------------+
| begin | end | capacity=10 |
+-------+-----+-------------+
| |
|/-----/
|
V
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| 2.2 | ??? | ??? | ??? | ??? | ??? | ??? | ??? | ??? | ??? |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
Next,
std::cout << "Vector size: " << aVector.size() << std::endl;
will of course give 0
since that's the difference between begin
and end
.
aVector.push_back(0.1);
The member push_back
will first check if there's space for another element after end
(which there clearly is in this case), reallocate if not (not applicable now) and then simply construct a new object of that value at *end
and increment end
. So now your vector looks like this:
+-------+-----+-------------+
| begin | end | capacity=10 |
+-------+-----+-------------+
| |
| //
| |
V V
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| 0.1 | ??? | ??? | ??? | ??? | ??? | ??? | ??? | ??? | ??? |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
As you can see, for the vector your 2.2
effectively didn't exist, and thus it was overwritten with the newly constructed 0.1
.
As the rest of the program just repeats previous steps (with the same results), I stop here.
Now if at the beginning you didn't request any elements, the vector will not initially allocate memory, and thus trying to write to aVector[0]
will cause a segmentation fault.
Upvotes: 8
Reputation: 2949
As of the doc, clear()
invalidates everything (references, pointers...) to any element of the std::vector
object, but does not deallocates memory since capacity()
remains unchanged.
Upvotes: 0