Why can't we allocate dynamic memory on the stack?

Allocating stuff on the stack is awesome because we have RAII and don't have to worry about memory leaks and the like. However, sometimes we have to allocate a heap:

  • If the data is really large (recommended) - because the stack is small.

  • If the size of the allocated data is known only at runtime (dynamic allocation).

Two questions:

  • Why can't we allocate dynamic memory (i.e. memory of a size that is only known at runtime) on the stack?

  • Why can we only refer to memory in heap pointers, while memory on the stack can be pointed through an ordinary variable? That is Thing t;

    .

Edit: I know that some compilers support Variable Length Arrays - heap stack overflow. But this is really an exception to the general rule. I'm interested in understanding the underlying reasons why in general we can't allocate heap on the stack - the technical reasons for it and the rational behind it.

+3


source to share


5 answers


Why can't we allocate dynamic memory (i.e. memory of size that is only known at runtime) on the stack?

This is more difficult to achieve. The size of each frame stack is burned into your compiled program as a consequence of the instructions that the finished executable must execute in order to work. For example, layout and a lot more of your functions - local variables are literally hardcoded into your program through the register and memory addresses that it describes in its low-level assembler: "variables" don't really exist in the executable. Making the number and size of these "variables" change between compilation runs makes this much more difficult, although this is not entirely impossible (with non-standard variable length arrays, as you discovered).

Why can we only refer to memory in heap pointers, whereas memory on the stack can only be referenced through an ordinary variable



It's just a consequence of the syntax. C ++ "normal" variables are those that have automatic or static storage times. The language designers could technically do it so that you could write something like Thing t = new Thing

and just use it t

all day, but they didn't; again, this would be more difficult to implement. How do you then distinguish between different types of objects? Remember that your compiled executable must remember to automatically destroy one view and not the other.

I would like to elaborate on why and why not all of this is difficult, since I believe that you are here. Unfortunately, my knowledge of assembly is too limited.

+4


source


Why can't we allocate dynamic memory (i.e. memory of size that is only known at runtime) on the stack?

This is technically possible. But not endorsed by the C ++ standard. Variable Length Arrays (VLA) let you create dynamically sized constructs on the stack. Most compilers allow this as a compiler extension.

example:

int array[n];

//where n is only known at run-time

      



Why can we only refer to memory in heap pointers, while memory on the stack can be pointed through an ordinary variable? That is Thing t;

.

We can. Whether you do it or not depends on the implementation details of the particular task.

Example:

int i;
int *ptr = &i;

      

+3


source


Each variable that has a name after compilation becomes a dereferenced pointer whose address value is computed by adding (depending on the platform, may be "subtract" ...) the "offset value" to the stack pointer (the register containing the address to which it actually reaches stack: this usually stores the "return address of the current function").

int i,j,k;

      

becomes

(SP-12) ;i
(SP-8) ;j
(SP-4) ;k

      

For this "sum" to be effective, the offsets must be constant so that they can be encoded directly in the op-code of the instruction:

k=i+j;

      

become

MOV (SP-12),A;   i-->>A
ADD A,(SP-8) ;   A+=j
MOV A,(SP-4) ;   A-->>k

      

You can see here how 4,8 and 12 are now "codes" and not "data".

This means that a variable that comes after another requires the β€œother” to keep a fixed compile-time size.

Dynamically declared arrays can be an exception, but they can only be the last variable of a function. Otherwise, all subsequent variables will have an offset, which must be adjusted at runtime after the array is allocated.

This creates a complication where dereferencing addresses requires arithmetic (rather than just equal offset) or the ability to modify the opcode as declared variables (self-modifying code).

Both solutions become suboptimal from a performance standpoint, as they can all break the locality of addressing or add more computation for each variable access.

+2


source


We can dynamically allocate variable length space in stack memory using the _alloca function. This function allocates memory from the program stack. It just takes the number of bytes to allocate and returns void * to the allocated space just like calling malloc. This allocated memory will be automatically freed when the function exits.

Thus, it does not need to be explicitly released. The size of the distribution must be kept in mind here, as an exception may occur. Exception handling can be used for such calls. In case of an exception, you can use _resetstkoflw () to restore it.

So our new code with _alloca will be:

 int NewFunctionA()
 {
  char* pszLineBuffer = (char*) _alloca(1024*sizeof(char));
  …..
  // Program logic
   ….
 //no need to free szLineBuffer
 return 1;
}

      

+2


source


Why can't we allocate dynamic memory (i.e. memory of size that is only known at runtime) on the stack?

You can use Microsoft compilers using _ alloca () or _ malloca () . For gcc, it's alloca ()

I'm not sure if this is part of the C / C ++ standards, but variants of alloca () are included in many compilers. If you need to align an allocation, such "n" bytes of memory, starting at the boundary of "m" bytes (where m is cardinality 2), you can allocate n + m bytes of memory, add m to the pointer, and turn off the lower bit mask. An example for placing 1000 hexadecimal bytes of memory on the boundary of a hexadecimal value. You don't need to store the value returned by _alloca () as it is on the memory stack and is automatically deallocated when the function exits.

char *p;
    p = _alloca(0x1000+0x100);
    (size_t)p = ((size_t)0x100 + (size_t)p) & ~(size_t)0xff;

      

+1


source







All Articles