0

Suppose we have an array int arr[size] and a pointer to an integer int *i. We can assign a set of sequential blocks of memory to the *i using malloc(sizeof(int) * size). However my question is how is memory allocated in int arr[size]. The assigning of memory blocks according to my guess in this case is done by the compiler. Does the compiler implicitly makes a malloc() or similar function call? And how is the dynamic memory allocation done by a compiler when we write something like int arr[] = "You guys are a great help".

Anagh Basak
  • 147
  • 9
  • 2
    `int arr[size]`, if size is a compile time constant, memory allocation is done before program execution begins. Otherwise, it's in the stack of the function call in which it appears. – Sourav Ghosh Nov 03 '20 at 04:54
  • 2
    For most systems local variables, including arrays, are stored on the stack. The compiler generates code to reserve a chunk of the stack for the variables (usually by manipulating one or more CPU registers) when the function is called. – Some programmer dude Nov 03 '20 at 04:57
  • @Someprogrammerdude Is the reservation of the memory in stack done on a basis of assumption? OR, does the compiler does some mathematics to understand how much of space the local variable of a particular function might need. I don't know this just an assumption by me, is stackoverflow the condition when the local variables uses more space than the space assigned to it? So any dynamically allocated memory is done from stack? – Anagh Basak Nov 03 '20 at 05:50
  • 1
    The compiler know the number of variables in a function, and the size of each variable (including arrays, whose size is basically calculated just like when you use `malloc`). – Some programmer dude Nov 03 '20 at 06:20
  • 1
    A stack overflow is when your program goes outside the stack area created by the compiler. For example by indexing out of bounds of a local array. – Some programmer dude Nov 03 '20 at 06:21
  • 1
    Lots of misconceptions here. See [What gets allocated on the stack and the heap?](https://software.codidact.com/questions/277536) for clarification of where variables are stored. – Lundin Nov 03 '20 at 07:34

1 Answers1

1

There's 4 different cases.

a) Global variables and static local variables. For these, the array is typically built into the executable program's file (which is typically split into sections - one for code, one for initialized read-only data, one for initialized data, and one for "initialized to zero"/uninitialized data). Mostly, if the array is initialized (e.g. int foo[44] = { 1, 2, 3 };) then it'll be put into the normal read/write data section (.data) and if it's not initialized (e.g. int foo[44];) it'll be put in the "initialized to zero"/uninitialized data section (.bss) to reduce the file's size. Allocating space in (e.g.) a .data section isn't much different to allocating space for code in an executable file's .text section. In this case the memory can not be freed.

b) Local variables (inside a function, like void foo(void) { int bar[44]; }). In this case the compiler will typically create space on the stack for it (and it won't be "initialized to zero"). If it's explicitly initialized then the compiler will generate code to initialize it, either using instructions (if the array is small), or by copying data from a hidden copy in a data section (where the hidden copy is allocated in the same way it would've been for a global variable). In this case the memory can not be explicitly freed but will be implicitly freed (if/when you return from the function).

c) Thread local storage (e.g. __thread int foo[]). In this case the compiler typically allocates space in thread local storage; and if the array is initialized it will also create a hidden copy in a data section (where the hidden copy is allocated in the same way it would've been for a global variable). In this case the memory can not be freed.

d) Dynamically allocated arrays (e.g. int *myPointer = malloc(sizeof(int) * 44);). For these, the compiler doesn't allocate space anywhere (the run-time library does). This is the only case where you can explicitly free the memory (e.g. free(myPointer);).

Note 1: A lot of the above (everywhere I've used "typically") is implementation dependent and a compiler may do something very different; either because "do it different" makes more sense for the compiler's output, or because the compiler can optimize properly.

Note 2: Sadly most compilers can't optimize properly. It's hard to get them to use a .rodata section when arrays are not modified, even if you use const. For cases where there's a hidden array used for initialization it's hard to get them to analyze the contents and find more complex patterns (they can/will do this badly for simple patterns like int foo[] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; }). If you have a global array like int hugeArray[12345678] = {4, 5, 6} they won't put the array in the .bss section and initialize it to reduce file size even when it would be trivial to do so, etc).

Brendan
  • 35,656
  • 2
  • 39
  • 66