C++ - How are multi-dimensional vectors stored? - c++

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

Related

How c++ manages the memory of a container in class?

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.

storing addresses of std::list elements; memory

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

How to access a vector array?

std::vector<int> example[1024];
How can I access all 1024 "examples" and their vector elements?
When I try example[0] it is the same like example.at(0) and I access the the first element in a vector...
I want to do the same as with variables:
int variable[1024]; ... but instead of an integer there is a vector...
It is not really clear what do you want to accomplish, but generally std::vector works pretty like classic C++ array.
And all the elements go one after another in memory, so you can do this for example
std::vector<int> test(1024);
test[0] = 1;
test[1] = 4;
test[2] = 8;
int* first = &test[0];
std::cout << "First is " << *first << std::endl;
int* second = first + 1;
std::cout << "Second is " << *second << std::endl;

Memory deallocation in std::vector

I'm trying to figure out how the memory management for std::vector works when you remove elements. It is my understanding that once memory was allocated for the vector, no pop_back\erase action would reduce the capacity of the vector, and the only way to do so is using shrink_to_fit which was introduced in C++ 11. Please correct me if I'm wrong.
You are correct, mostly.
There are only a few ways to reduce a std::vector's capacity…
Move Assignment
#include <iostream>
#include <vector>
int
main()
{
std::vector<int> v1(10);
std::vector<int> v2(20);
std::cout << "v2.capacity() = " << v2.capacity() << '\n';
std::cout << "Move assign v2 with less capacity\n";
v2 = std::move(v1);
std::cout << "v2.capacity() = " << v2.capacity() << '\n';
}
which should output:
v2.capacity() = 20
Move assign v2 with less capacity
v2.capacity() = 10
If the allocator involved is set a certain way, diminished capacity is not assured. But in the common case as shown above, capacity will shrink. If you really want to know about the nitty gritty details of how capacity might not shrink during move assignment, ask another question, and I can dig into that in detail.
shrink_to_fit
#include <iostream>
#include <vector>
int
main()
{
std::vector<int> v(20);
std::cout << "v.capacity() = " << v.capacity() << '\n';
std::cout << "erasing...\n";
v.erase(v.begin() + v.size()/2, v.end());
std::cout << "v.capacity() = " << v.capacity() << '\n';
std::cout << "shrink_to_fit...\n";
v.shrink_to_fit();
std::cout << "v.capacity() = " << v.capacity() << '\n';
}
which will probably output:
v.capacity() = 20
erasing...
v.capacity() = 20
shrink_to_fit...
v.capacity() = 10
But it is allowed to output:
v.capacity() = 20
erasing...
v.capacity() = 20
shrink_to_fit...
v.capacity() = 20
swap
#include <iostream>
#include <vector>
int
main()
{
std::vector<int> v(20);
std::vector<int> v2(10);
std::cout << "v.capacity() = " << v.capacity() << '\n';
std::cout << "swap...\n";
swap(v, v2);
std::cout << "v.capacity() = " << v.capacity() << '\n';
}
which should output:
v.capacity() = 20
swap...
v.capacity() = 10
I know of no other ways to shrink the capacity of a vector (besides obviously equivalent constructs such as v.swap(v2)).
A good rule of thumb is that the capacity of a vector never shrinks unless:
It transfers resources with another vector (e.g. move assignment or swap).
shrink_to_fit is called.
Oh, and I nearly forgot a third possibility: any time you move from a vector, the moved-from vector might have a diminished capacity (or it might not). It is unspecified, so you can't count on it. In general moved-from values should be variables where you don't care what their current state is. You will subsequently destruct them or assign them a new known state (assignment doesn't have to be via the assignment operator; could be clear() followed by push_back() for example).

push_back versus operator[] assignment in c++ vectors

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