Home > Software design >  Why does malloc need to be used for dynamic memory allocation in C?
Why does malloc need to be used for dynamic memory allocation in C?

Time:09-23

I have been reading that malloc is used for dynamic memory allocation. But if the following code works...

int main(void) {
        int i, n;
        printf("Enter the number of integers: ");
        scanf("%d", &n);

        // Dynamic allocation of memory?
        int int_arr[n];

        // Testing
        for (int i = 0; i < n; i  ) {
                int_arr[i] = i * 10;
        }
        for (int i = 0; i < n; i  ) {
                printf("%d ", int_arr[i]);
        }
        printf("\n");
}


... what is the point of malloc? Isn't the code above just a simpler-to-read way to allocate memory dynamically?

I read on another Stack Overflow answer that if some sort of flag is set to "pedantic", then the code above would produce a compile error. But that doesn't really explain why malloc might be a better solution for dynamic memory allocation.

CodePudding user response:

Just because something looks prettier does not make it a better choice.

VLAs have a long list of problems, not the least of which they are not a sufficient replacement for heap-allocated memory.

The primary -- and most significant -- reason is that VLAs are not persistent dynamic data. That is, once your function terminates, the data is reclaimed (it exists on the stack, of all places!), meaning any other code still hanging on to it are SOL.

Your example code doesn't run into this problem because you aren't using it outside of the local context. Go ahead and try to use a VLA to build a binary tree, then add a node, then create a new tree and try to print them both.

The next issue is that the stack is not an appropriate place to allocate large amounts of dynamic data -- it is for function frames, which have a limited space to begin with. The global memory pool, OTOH, is specifically designed and optimized for this kind of usage.

It is good to ask questions and try to understand things. Just be careful that you don't believe yourself smarter than the many, many people who took what now is nearly 80 years of experience to design and implement systems that quite literally run the known universe. Such an obvious flaw would have been immediately recognized long, long ago and removed before either of us were born.

VLAs have their place, but it is, alas, small.

CodePudding user response:

Declaring local variables takes the memory from the stack. This has two ramifications.

  1. That memory is destroyed once the function returns.
  2. Stack memory is limited, and is used for all local variables, as well as function return addresses. If you allocate large amounts of memory, you'll run into problems. Only use it for small amounts of memory.

CodePudding user response:

When you have the following in your function code:

int int_arr[n];

It means you allocated space on the function stack, once the function will return this stack will cease to exist.

Image a use case where you need to return a data structure to a caller, for example:

Car* create_car(string model, string make)
{
    Car* new_car = malloc(sizeof(*car));
    ...
    return new_car;

}

Now, once the function will finish you will still have your car object, because it was allocated on the heap.

CodePudding user response:

The memory allocated by int int_arr[n] is reserved only until execution of the routine ends (when it returns or is otherwise terminated, as by setjmp). That means you cannot allocate things in one order and free them in another. You cannot allocate a temporary work buffer, use it while computing some data, then allocate another buffer for the results, and free the temporary work buffer. To free the work buffer, you have to return from the function, and then the result buffer will be freed to.

With automatic allocations, you cannot read from a file, allocate records for each of the things read from the file, and then delete some of the records out of order. You simply have no dynamic control over the memory allocated; automatic allocations are forced into a strictly last-in first-out (LIFO) order.

You cannot write subroutines that allocate memory, initialize it and/or do other computations, and return the allocated memory to their callers.

(Some people may also point out that the stack memory commonly used for automatic objects is commonly limited to 1-8 mebibytes while the memory used for dynamic allocation is generally much larger. However, this is an artifact of settings selected for common use and can be changed; it is not inherent to the nature of automatic versus dynamic allocation.)

CodePudding user response:

Look up the concepts for stack and heap; there's a lot of subtleties around the different types of memory. Local variables inside a function live in the stack and only exist within the function.

In your example, int_array only exists while execution of the function it is defined in has not ended, you couldn't pass it around between functions. You couldn't return int_array and expect it to work.

malloc() is used when you want to create a chunk of memory which exists on the heap. malloc returns a pointer to this memory. This pointer can be passed around as a variable (eg returned) from functions and can be used anywhere in your program to access your allocated chunk of memory until you free() it.

Example:

'''C

int main(int argc, char **argv){
    int length = 10; 
    int *built_array = make_array(length); //malloc memory and pass heap pointer
    int *array = make_array_wrong(length); //will not work. Array in function was in stack and no longer exists when function has returned.

    built_array[3] = 5; //ok
    array[3] = 5; //bad 

    free(built_array) 

    return 0;  
 }

int *make_array(int length){
    int *my_pointer = malloc( length * sizeof int);
    //do some error checking for real implementation 
    return my_pointer; 
}

int *make_array_wrong(int length){
    int array[length]; 
    return array; 
 }

'''

Note: There are plenty of ways to avoid having to use malloc at all, by pre-allocating sufficient memory in the callers, etc. This is recommended for embedded and safety critical programs where you want to be sure you'll never run out of memory.

CodePudding user response:

If the allocated memory is small and used only inside the function, malloc is indeed unnecessary. If the memory amount is extremely large (usually MB or more), the above example may cause stack overflow. If the memory is still used after the function returned, you need malloc or global variable (static allocation).

Note that the dynamic allocation through local variables as above may not be supported in some compiler.

  • Related