In c++, when all the memory allocated to a container (say vector) is used up (and we are trying to add one more element), the memory will be reallocated. However, I was wondering that how class in c++ manages the memory for containers.
For example, I run the following code:
#include <iostream>
#include <vector>
class Test{
public:
int i = 0;
std::vector<int> v;
};
int main(){
Test t;
std::cout << "Address of t: " << &t << ", capacity of vector: " << t.v.capacity() << ", size of vector: " << t.v.size() << ", address of vector: " << &(t.v) << std::endl;
t.v.push_back(1);
std::cout << "Address of t: " << &t << ", capacity of vector: " << t.v.capacity() << ", size of vector: " << t.v.size() << ", address of vector: " << &(t.v) << std::endl;
t.v.push_back(2);
std::cout << "Address of t: " << &t << ", capacity of vector: " << t.v.capacity() << ", size of vector: " << t.v.size() << ", address of vector: " << &(t.v) << std::endl;
return 0;
}
And the output is:
Address of t: 0x61fee8, capacity of vector: 0, size of vector: 0, address of vector: 0x61feec
Address of t: 0x61fee8, capacity of vector: 1, size of vector: 1, address of vector: 0x61feec
Address of t: 0x61fee8, capacity of vector: 2, size of vector: 2, address of vector: 0x61feec
The address of the vector is not changed. Does it mean the c++ uses a pointer to represent each data member (so address 0x61feec actually points to the address of the vector)?
The address of the vector is not changed.
Correct, because the vector itself is not moving around in memory.
Does it mean the c++ uses a pointer to represent each data member (so address 0x61feec actually points to the address of the vector)?
Everything in memory has an address.
The std::vector class internally contains a data member that is a pointer to an array of elements. The vector::size() method reports the number of valid elements in the array, while the vector::capacity() method reports the maximum number of elements the array is allocated to hold. The vector (re-)allocates that array dynamically as needed, ie whenever the size() is equal to the capacity() when adding new elements.
Nothing in your example code is printing the address of that array itself. The vector::data() method returns a pointer to that array. Add that pointer to your logging, and you will see it change value as the capacity() changes over time, eg:
void log(const Test &t)
{
std::cout << "Address of t: " << &t
<< ", capacity of vector: " << t.v.capacity()
<< ", size of vector: " << t.v.size()
<< ", address of vector: " << &(t.v)
<< ", address of vector data: " << t.v.data()
<< std::endl;
}
int main(){
Test t;
log(t);
for(int i = 1; i <= 50; i++)
{
t.v.push_back(i);
log(t);
}
return 0;
}
The memory is embedded into the containing class. Let's look at an easy example:
struct myvector {
size_t size;
void* data;
}
class Test {
myvector v;
}
In this case, new objects of class Test will be allocated with sizeof(size_t)+sizeof(void*) bytes.
Now when it comes to resizing the vector, all that is done, is a realloc() on the memory pointed to by data.
Of course, the std::vector implementation is far more complicaten than that, but I think you get the idea.
Related
I am creating std::list of struct elements. With a certain criterion, I want to store addresses of few elements (because those addresses don't change(?)) from the list into std::vector for quick access in another usage. An example of the things is given below
#include <iostream>
#include <vector>
#include <list>
struct Astruct{
double x[2];
int rank;
};
int main(int argc, char *argv[]) {
std::list<Astruct> ants;
std::vector< Astruct* > ptr;
for (auto i = 0; i != 20; ++i) {
Astruct local;
local.x[0] = 1.1;
local.x[1] = 1.2;
local.rank = i;
// put in list
ants.push_back(local);
// store address of odd numbers
// rather than temperory address, permenent address from list is needed
if(local.rank %2 == 0) ptr.push_back(&local);
}
// print the selected elements using addresses from the list
for(int num = 0; num != ptr.size(); num++){
Astruct *local;
local = ptr.at(num);
std::cout << " rank " << local->rank << "\n";
}
/*
// quick way to check whether certain address (eg 3rd element) exists in the std::vector
std::list<Astruct>::iterator it = ants.begin();
std::advance(it , 2);
for(int num = 0; num != ptr.size(); num++){
if(it == ptr.at(num)) std::cout << " exists in vector \n " ;
}
*/
// print memory in bytes for all variables
std::cout << " sizeof Astruct " << sizeof(Astruct) << "\n";
std::cout << " sizeof ants " << sizeof(ants) << "\n";
std::cout << " sizeof ptr " << sizeof(ptr) << "\n";
}
What's the way to access an address of a particular element from the list?
Is it efficient method to add elements to list? (in first for loop)
What is the quickest way to check whether certain address exists in the vector? (shown in comment block)
How to determine the memory size in bytes for different variables here? (end of the code)
Thanks.
What's the way to access an address of a particular element from the list?
address=&(*iterator);
Is it efficient method to add elements to list? (in first for loop)
the first loop does not use the list at all! (Ah, OK, after edition it does)
all the addresses which are stored in the vector refer to a local variable which disappears after each iteration; this is undefined behaviour (very probably, but nothing is certain, all these addresses are the same)
What is the quickest way to check whether certain address exists in the vector? (shown in comment block)
usualy std::find() from <algorithm> is suitable.
How to determine the memory size in bytes for different variables here? (end of the code)
std::cout << " sizeof Astruct " << sizeof(Astruct) << "\n"; is OK
std::cout << " sizeof ants " << size(ants)*sizeof(Astruct) << "\n"; is an approximation since we don't know the overhead of the list and its nodes
std::cout << " sizeof ptr " << size(ptr)*sizeof(Astruct *) << "\n"; is an approximation since we don't know the overhead of the vector
Assigning a local variable vector to a map<int , std::vector<int>> m1 in foo(), hoping that the value of s1 cannot be accessible once it goes out of scope. But that is not the case. Looks like the elements in vector are stored in heap memory and local variable s1 is stored in stack. when s1 was stored in map, it looks like it allocated a new heap memory and copied the values to it. Is my understanding right?
I am printing the address of each vector element in foo and also the address of each vector element in map.
#include <iostream>
#include <map>
#include <set>
#include<vector>
using namespace std;
std::map<int , std::vector<int>> m1;
void foo(){
vector<int> s1 = { 10, 20, 30, 40 };
cout << "local var address: " << &s1 << "\n";
cout << "Element address " << &s1[0] << " " << &s1[1] << " "
<< &s1[3] << " " << &s1[4] << "\n";
m1[1] = s1;
}
int main() {
foo();
cout << "\nElement value and address in map:\n";
for (auto it = m1[1].begin(); it != m1[1].end();it++) {
cout << *it << " " << &m1[1][*it] << "\n";
}
return 0;
}
output:
local var address: 0x7fff41714400
Element address 0xc07c20 0xc07c24 0xc07c2c 0xc07c30
Element value and address in map:
10 0xc08cc8
20 0xc08cf0
30 0xc08d18
40 0xc08d40
When you do m1[1] = s1;, you're calling m1[1]'s assignment operator. If you follow that link, you're calling the first instance, which cppreference describes as:
1) Copy assignment operator. Replaces the contents with a copy of the contents of other.
(emphasis mine)
So you're looking at the addresses of two completely different vectors and two completely different sets of items. It makes no sense to compare them.
std::map<int , std::vector<int>>
has a value_type of std::vector<int>. That means that each item stored in the map contains such a vector object.
It doesn't contain a reference to a vector, or a pointer to a vector, it's actually an object.
That means that if you create a vector outside the map, and assign it to a map element, it must be moved or copied.
I am learning how vectors work in c++, and wrote a sample program to try to learn how memory with vectors are handled.
#include <iostream>
#include <vector>
int main()
{
//Test 1:
double n = 3.5;
std::vector<double> test;
std::cout << sizeof(test) << std::endl;
test.push_back(n);
std::cout << sizeof(test) << std::endl;
std::cout << std::endl;
std::cout << std::endl;
std::cout << std::endl;
//Test 2
std::vector<int> test2;
std::cout << sizeof(test2) << std::endl;
for (int i = 0; i < 1000; i++) {
test2.push_back(i);
}
std::cout << sizeof(test2) << std::endl;
}
Interestingly, the program prints out 24 as the number of bytes stored each-time. Despite adding new elements to the vector. How is the amount of memory that the vector occupies when it is initially declared the same as after I have added elements to the vector?
Internally, the vector object has a pointer to dynamically-allocated memory that contains the elements. When you use sizeof(test) you're just getting the size of the structure that contains the pointer, the size of the memory that it points to is not included.
This memory has to be dynamically-allocated so that the vector can grow and shrink as needed. It's not possible for a class object to change its size.
To get the amount of memory being used by the data storage, use sizeof(double) * test.capacity().
I have two questions with respect to vectors.
Let us say I have a multi-dimensional vector as follows :-
vector< vector<int> > A;
Then A[0], A[1], etc are vectors. How are the vectors stored in A? I
mean what information about the vectors A[0] and A[1] is stored in A?
And does memory re-allocation of the individual vectors such as A[2]
cause re-allocation of A as well?
Second, I tried to see how the addresses of a vector change with re-allocation. I used the following code:-
Code:
vector<int> A;
int* x ;
int* y ;
vector<int>* ad;
vector<int>* bd;
for(int i = 0 ; i < 10000; i++){
A.push_back(i);
if(i == 2){
y = &A[0];
ad = &A;
}
x = &A[0];
bd = &A;
}
I found that, the address for A does not change even though the address for A[0] changes. This is to be expected since vectors work in the background by making use of new and delete. But my question is how much information (or which information) about the vector is stored in the address &A (considering the address of &A does not change ). This is the question I have with respect to the first question as well.
I am trying to better understand how vectors work by default.
how much information (or which information) about the vector is stored in the address &A
You are correct in assuming that the data for the vector is stored separately from the vector object itself - typically, in dynamic memory.
The three things the vector object itself needs to know are
The location of the vector's data - we need this to perform the [] operator,
The size currently allocated - we need this to know when to grow the array, and
The number of elements actually placed into the vector - we need this to know where to push_back, and what to return from size().
Different implementations are possible, storing as little as a single pointer in the vector object itself. However, a typical implementation stores a pointer to the beginning of the allocated block, a pointer to the end of the active part of the allocated block, and a pointer to the end of the allocated block.
Regarding the vector's address: The address of A does not change, not because A is a vector, but because no variable's address changes while the function where you define it (or rather, the specific call of your function) is executing. I think you may be confusing A's address (ad, bd in your example) with the address of what A uses to store the elements of the vector (x and y, essentially, in your example). Vectors allocate, de-allocate or re-allocate storage.
Note that A[0] is not a variable that you defined. It is the result of the invocation of A.operator[]; so its location can change.
Regarding what's actually stored at &A: That is kind of complicated. You will need to look at the header file vector within your C++ installation. Or perhaps it would be better to have a look at the webpage for std::vector at cppreference.com. Note there's a lot of templates, and some subclassing, and some explicit template specializations, so like I said - complicated. You might want reconsider whether you really want to look under the hood just to understand how this container works as a general rule, or whether the class' public methods and sizeof() figure are sufficient for now.
I am a beginner in c++ and STL, so I just test your problem with some simple codes;
first, i have these codes:
std::vector<int> tmp;
std::cout << sizeof(tmp) << " " << tmp.size() << " " << tmp.capacity << std::endl;
the output is:
12 0 0
then, we insert some values into the vector
for(int i = 0; i != 10; ++i) tmp.push_back(i);
std::cout << sizeof(tmp) << " " << tmp.size() << " " << tmp.capacity << std::endl;
the output is
12 10 16
then, we can draw a conclusion that the vector just keep a pointer, so the sizeof() result didn't changed.
So, the answer of your question is , the child vector's push_back will not result in the reallocation of the parent vector(i don't know how to express the role of these two vectors).
There are some simple codes:
std::vector<int> v1(10);
std::vector<int> v2(10);
int i;
for(i = 0; i != 10; ++i)
v1[i] = i;
for(i = 0; i != 10; ++i)
v2[i] = i;
vv.push_back(v1);
vv.push_back(v2);
std::cout << "v1 capacity: " << v1.capacity() << " v1 size: " << v1.size() << std::endl;
std::cout << "v2 capacity: " << v2.capacity() << " v2 size: " << v2.size() << std::endl;
std::cout << "vv capacity: " << vv.capacity() << " vv size: " << vv.size() << std::endl;
for(i = 10; i != 20; ++i)
v1.push_back(i);
for(i = 10; i != 20; ++i)
v2.push_back(i);
std::cout << "v1 capacity: " << v1.capacity() << " v1 size: " << v1.size() << std::endl;
std::cout << "v2 capacity: " << v2.capacity() << " v2 size: " << v2.size() << std::endl;
std::cout << "vv capacity: " << vv.capacity() << " vv size: " << vv.size() << std::endl;
output:
v1 capacity: 10 v1 size: 10
v2 capacity: 10 v2 size: 10
vv capacity: 2 vv size: 2
v1 capacity: 20 v1 size: 20
v2 capacity: 20 v2 size: 20
vv capacity: 2 vv size: 2
can someone please explain to me in detail why the following code for vectorY will do the assignment but the size of VecY is zero? Also, the begin and end iterators are stuck at the first node. It seems that reserve only works with push back and that you need to construct the vector with the size if you want the iterators for the vectors and the size to work as expected. I am assuming that push_back is doing some type of allocation that the straight assignment is not in this case? I am looking for details explaining this so I can make sure I understand what is happening with a reserve and push_back versus constructing with a size element and then doing assignment as in VecX example.
#include <iostream>
#include <vector>
int main ( int argc, char *argv[])
{
std::vector<int> vecX(2);
vecX[0] = 1;
vecX[1] = 2;
std::cout << " VecX0 Item: " << vecX[0] << std::endl;
std::cout << " VecX1 Item: " << vecX[1] << std::endl;
std::cout << " VectorX Size: " << vecX.size() << std::endl;
std::vector<int> vecY;
vecY.reserve(2);
vecY[0] = 1;
vecY[1] = 2;
std::cout << " VecY0 Item: " << vecY[0] << std::endl;
std::cout << " VecY1 Item: " << vecY[1] << std::endl;
std::cout << " VectorY Size: " << vecY.size() << std::endl;
}
Output
VecX0 Item: 1
VecX1 Item: 2
VectorX Size: 2
VecY0 Item: 1
VecY1 Item: 2
VectorY Size: 0
std::vector<int> vecY;
vecY.reserve(2);
vecY[0] = 1;
vecY[1] = 2;
This code is wrong and evokes Undefined Behavior1. When you reserve a vector, you set the capacity, not the size.
You need to either push_back, or construct the vector as you did in example 1.
"Undefined Behavior" : This invokes Undefined Behavior because of the out-of-range call to operator[] If you call vector::operator[n] where n > vec.size(), the behavior is Undefined.
If you don't want use push_back nor construct, consider using the resize method