3

Consider the two codes below :

#include<stdio.h> 
void main() {
   int num; 
   scanf("%d",&num); 
   int arr[num]; 
   for(int i=0;i<num;i++){
      arr[i] = i+1;
   }
}
#include<stdio.h> 
#include<stdlib.h>
void main() {
   int num; 
   scanf("%d",&num); 
   int*p=(int*)malloc(num*size of(int));
   for(int i=0;i<num;i++){
      *(p+i) = i+1;
   }
}

Above two codes both allocates memory for array of size of num which is given by user and then initialise it . But first one is having the size of the array to be variable , so at compile time the compiler gives some memory space which is not(maybe) exactly num but maybe more than that but while at run time the num size gets allocated to it later on .

While for the case of malloc one there was no such problem of having a random memory space being allocated initially and hence that's how we can say that malloc is better in terms of this feature and hence more suited for use ?

coder1229
  • 33
  • 5
  • Scope... A VLA only exists in the scope of its instantiation. An allocated block of heap storage exists until it is free'd. Most programs are more _intricate_ than your small example... Plus, a dynamic memory block can be resized. – Fe2O3 Mar 31 '23 at 03:11
  • I understood that scope nature is different but how is that advantageous ? As in why scope having to be freed by us is considered more beneficial ? @Fe2O3 – coder1229 Mar 31 '23 at 03:16
  • 1
    Also, the stack size is fairly limited. Failure to allocate stack memory typically crashes the app with a stack overflow and is not recoverable. The heap can hold much more data, and apps can handle heap allocation failures without crashing. In the above example, if someone enters 1000000, the first programs will crash but the second will run. – Raymond Chen Mar 31 '23 at 03:17
  • imagine a program calling a function to load an unknown amount of data from a file. A VLA in the function will not work. However, the function can allocate and grow as much storage as is needed, then return the address of that storage for use. (Always remember to free().) – Fe2O3 Mar 31 '23 at 03:19
  • Good point thanks for mentioning that @RaymondChen , also is that bold text which I said true? That is i mean random memory space allocated at compile time in first program ? And later on at run time it gets the value which the user inputs ? – coder1229 Mar 31 '23 at 03:20
  • Hmm understood now @Fe2O3 thanks , one more thing you said we can resize in dynamic one , but that can also be done in first program as in we can just put num+2 or something ? Like we did with realloc? – coder1229 Mar 31 '23 at 03:24
  • 1
    "_ we can just put num+2 or something_"... The best way to learn is to try things out for yourself. You can **define** a larger VLA, but you can only define it once... – Fe2O3 Mar 31 '23 at 03:25
  • @RaymondChen: On Linux, I believe the default stack size is typically about 8 MB, which is more than sufficient to store an array of one million `int` elements. Therefore, I believe your claim that "the first program will crash" if the user enters `1000000` is not necessarily true. However, it is true on Windows, where the default stack size is only about 1 MB. – Andreas Wenzel Mar 31 '23 at 03:53
  • Side note: In contrast to C++, in C, it is not recommended to cast the result of `malloc`. You may want to read this: [Do I cast the result of malloc?](https://stackoverflow.com/q/605845/12149471) – Andreas Wenzel Mar 31 '23 at 04:01
  • 1
    Either version needs to sanitize the input before allocating the array. Otherwise the code is very brittle. – Lundin Mar 31 '23 at 13:08
  • @Lundin: In the case of `malloc`, an alternative to sanitizing the input would be to check the return value of `malloc`. This should actually be done even when the input is valid. – Andreas Wenzel Mar 31 '23 at 13:15
  • @AndreasWenzel That should be done, but it is not a replacement for input sanitation. You have to ensure that insane amounts are not requested (or that the input is in ASCII etc), as well as avoiding the can of worms known as `malloc(0)`. – Lundin Mar 31 '23 at 13:21
  • @Lundin: I believe you are right. Another issue is that on Linux, checking the return value of `malloc` may not be sufficient, because [on default settings](https://www.kernel.org/doc/Documentation/vm/overcommit-accounting), Linux will over-commit memory, so that inputting a too high number will not necessarily cause `malloc` to return `NULL`, but rather for the process to get killed by the [Linux OOM killer](https://www.kernel.org/doc/gorman/html/understand/understand016.html) (out-of memory killer). – Andreas Wenzel Mar 31 '23 at 13:32

2 Answers2

2

Above two codes both allocates memory for array of size of num which is given by user and then initialise it

In the first case, arr is declared as a local variable, so it uses memory from the stack, and it exists only for the duration of the function in which it's declared. If that function is main, then of course it will exist for the life of the program, but arrays declared this way in other functions with much shorter lifetimes will likewise have shorter lifetimes.

In the second case, the array is declared "dynamically" -- it uses memory from the heap instead of the stack, and will continue to exist even after the function in which it was created exits. Because programs typically have much more heap space than stack space, using malloc can also be useful because it allows creation of larger arrays.

So, for example, it's very common for a program to operate on data that's read from a file, or perhaps from several files. Imagine writing a function to open a file, read the data into an array, and return the array to the caller. If that function declares the array as a local variable, it'll go out of scope when the function exits, and you won't be able to return it to the caller. Alternatively, you could have the caller create the array and pass it into the function that reads the data, but then the caller has to know ahead of time how much data is in the file, which isn't always possible. Furthermore, the programmer might not know in advance how many files, and therefore how many arrays, might be read. All these problems go away if you instead create an array dynamically each time the file-reading function is called: you get a new array for each file, you can easily return the array to the caller, and the array can be sized to suit the amount of data in the file.

Caleb
  • 124,013
  • 19
  • 183
  • 272
  • 2
    One important aspect, is that there is no way to gracefully handle stack overflow, that is one can't write any code to detect that this specific VLA can be allocated and not cause the overflow, unlike with `malloc` which is returning error that can be handled (well, not in case of overcomitting... but it is rather an OS quirk than C) – Eugene Sh. Mar 31 '23 at 03:32
  • Very nice explanation , just want to know one more thing . is this true about how compilation process happens in first case : That is i mean random memory space allocated at compile time in first program (when the compiler reads the int arr[num]? ) .And later on at run time it gets the memory space which the user inputs ? – coder1229 Mar 31 '23 at 03:35
  • @coder1229 No. In the first case, it's probably not hard to predict where `arr` will be allocated because it will be in the stack frame of `main`, which special in a way because it's the first (or neary first) function called. But for any function creating a local array, the array will appear in the stack frame for the function call, which is not random. Arrays created dynamically will whatever block of memory the allocator decides to use, which is also not random, but mostly unknowable for the programmer. The user input has nothing to do with the address of the array, though. – Caleb Mar 31 '23 at 03:42
  • Sorry what i meant was random space (that is size of array would be given maybe 2000 bytes at random or something by compiler )(compile time) but later on when user input (run time) like maybe information of 20 bytes then just 20 bytes gets allocated to it ? – coder1229 Mar 31 '23 at 03:50
  • Honestly, I haven't really kept up with how variable-length arrays are allocated. I'd think the compiler might use code similar to `alloca()`, but exactly what happens likely depends on your particular compiler. In any case, I'd expect any decent compiler to emit code that determines the needed space at runtime, not use some pre-determined large constant, because the variability is the whole point of using a VLA. – Caleb Mar 31 '23 at 04:17
2

Possible advantages of malloc():

  • it still exists after it falls out of scope (e.g. you can do myPointer = malloc(foo); return myPointer; but cannot do int myArray[foo]; return myArray;).

  • it can typically handle much larger allocations without problems (available heap is almost always larger than available stack space)

  • it allows you to handle errors gracefully (by returning NULL if there's a failure instead of causing undefined behaviour when something like int myArray[foo]; causes your stack to overflow).

  • the memory can be resized/reallocated after it was allocated.

Possible disadvantages of malloc():

  • it's typically slower when allocating and freeing the memory

  • it can be slower when using the memory due to locality

  • to avoid memory leaks you have to free() the memory later, and you must never free() the memory more than once. This takes some extra effort from the programmer to keep track (possibly including the use of tools like valgrind to make sure it's right).

Brendan
  • 35,656
  • 2
  • 39
  • 66