Array as a template parameter: stack or heap?

My stack knowledge is very rudimentary compared to a bunch, but when it comes to arrays, from what I know, something like this is created on the stack

float x[100]; 

while something like this is created on the heap

 float* x = new float[100]; 

But what happens if I create a class of an array of templates and pass it to an array of type "stack" (for example, float[100] )? Example:

 #include <iostream> using namespace std; template <class T> class Array { public: int size; T* data; Array(int size_) : size(size_) { data = new T[size]; } ~Array() { delete [] data; } }; int main() { int m = 1000000; const int n = 100; Array<float[n]>* array = new Array<float[n]>(m); for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) array->data[i][j] = i * j; cout << array->data[10][9] << endl; delete array; } 

What exactly is going on here? Is this memory created on the stack or on the heap? My guess is a bunch, but how does it work? Does the compiler allocate one large block of memory and then store pointers that index each element of n into it? Or does it allocate many smaller blocks of memory (not necessarily contiguous) and store pointers to each block?

Also, I cannot do this without the help of a template. In particular, this code does not compile:

 int m = 1000; const int n = 100; (float[n])* array = new (float[n])[m]; 

What's going on here?

EDIT:

Thanks for the syntax advice, everyone. I was really interested in what is happening in the block

 int m = 1000; const int n = 100; float (*array)[n] = new float[m][n]; 

but I did not know how to write it without using templates. The only thing that really interested me was that the compiler allocates this as one big block on the heap, how can you use the array[i][j] syntax to access a specific element without saving pointers to every nth element? Then I realized that since n constant, sizeof(float[n]) fixed, so when you create an array, the compiler allocates an array of elements m , where each element is float[n] , which in my case is 100 * 4 = 400 bytes. Now everything makes sense. Thanks!

+6
source share
4 answers

You wrote your arrays in the opposite direction. It works:

  int m = 1000; const int n = 100; float (*array)[n] = new float[m][n]; delete[] array; 

If you want to keep the extents of arrays in the same order, you can use a type alias or an appropriate pattern:

  using A = float[n]; A* array = new A[m]; 

or

 // at file scope template<typename T, unsigned N> using add_extent = T[N]; // ... add_extent<float, n>* array = new add_extent<float, n>[m]; 

A multidimensional array, whether on the stack or on the heap, is allocated as one block of m*n elements. When you index a pointer to an array type (for example, float (*array)[n] ), the pointer is incremented by n at a time per array step.

+2
source
 Array<float[n]>* array = new Array<float[n]>(m); 

What happens here is two heap allocations. An Array object will be allocated on the heap because you used new to create it. The new expression calls the Array constructor, which again uses new to allocate the data array; therefore data also allocated on the heap.

Better to do it:

 Array<float[n]> array(m); 

This allocates an Array on the stack (so it will be automatically destroyed at the end of the block). However, although the Array object itself is on the stack, data is still stored on the heap because it is allocated on the heap in the Array constructor. This is similar to what happens when you have a local variable std::vector or std::string .

Also, I cannot do this without the help of a template. In particular, this code does not compile:

This is simply because your syntax is incorrect. The correct syntax is:

 float (*array)[n] = new float[m][n]; 

The left side shows the correct way to declare a pointer to an array. For the right side, you need an array m float[n] s. This is denoted by float[m][n] ; [m] does not end.

+3
source

All memory is stored on the heap. The compiler allocates one giant chunk of memory for an array of arrays and adjusts the indexing so that it is available.

And if anyone ever copies or assigns your Array class, you will be a memory leak and / or double deletion.

+1
source

In line

 Array<float[n]>* array = new Array<float[n]>(m); 

An instance of Array<T> is allocated on the heap. You already understood that, given your first statement about the distribution as a whole.

Perhaps the confusing part is using float[n] as a template parameter?

The template parameter T , indicated by the class keyword in your Array definition, represents the type. It is not in itself associated with any form of distribution.

To prove this, write a simple template that does not use its parameter:

 #include <cassert> using namespace std; template <typename T> class A { }; int main(){ A<float[100]> a1; A<float[1000]> a2; float f[100]; assert(sizeof(a1) == sizeof(a2)); cout << "a1 : " << sizeof(a1) << endl; cout.<< "f : " << sizeof(f) << endl; } 

Output:

 a1 : 1 f : 400 

So, float[n] really is type (1) here .

On the other end, when you use the new keyword, you know that something stands out on the heap. As I said, the Array variable will point to a piece of memory on the heap. In addition, the template itself contains a heap distribution (again, the new keyword).

Finally, I would like to emphasize the basic premise that new indicates heap distribution. Although this happens by default when used in placement mode, the actual distribution may well be on the stack.


<sub> (1) Note that C ++ takes it as such, because n declared as a constant, and therefore the resulting type can be evaluated at compile time. Remove the const attribute of the definition of n and the compiler will complain. Sub>

0
source

Source: https://habr.com/ru/post/972411/


All Articles