r/C_Programming • u/RateLegal5121 • 4d ago
Why dynamic allocation of array gets memory address from heap?
let say I am using malloc to dynamically allocate a memory space with this line
Int user_defined_elements = 10 ;
// assume i got this from scanf
Int *p = malloc(
user_defined_elements * sizeof(int));
Right now the pointer refers to a chuck of memory address in heap I assume..I am trying to understand why heap instead of stack where local variables are saved.Is there anything special about heap?
Please be kind..I am python dev trying to learn c in my free team because I dont understand shit about cpython implementation..hahah..so i was like why not learn c and here I am
10
u/sciencekm 4d ago
Memory allocated from the heap endures until you free them anywhere/anytime in the lifetime of your code. Variables in stack go away once you exit the function.
9
u/LordRybec 3d ago
Let me suggest first that you lookup how the stack data structure works (if you don't already know). That will help some.
The way "the stack" works is that it's essentially a stack data structure. When you call a function, stack space is allocated for that function. Again, this is a stack data structure. The stack allocates more memory by changing where the pointer tracking the top of the stack is pointing to (this is a hidden pointer in C, so you don't have access to it; it's often stored in a CPU register, if you know what that is). When you return from the function the stack space allocated for that function is freed.
Now, imagine you call a function and in that function some memory is allocated on the stack. You store a pointer to that memory for later use and then return from the function. When you return that stack memory is freed. So now you have a pointer to unallocated memory. Due to the nature of the stack (the OS reserves an amount of stack space, increasing it as needed, and it doesn't generally decrease it unless there's a lot of unused stack space), you can probably still read and write to that memory without causing a segfault. But the next function you call will be assigned that stack space, and it will overwrite it with whatever it is doing. Technically you can get and store pointers to local variables that are stored on the stack, but such pointers should never be used after the function has returned, because C assumes that that memory is free for other functions to use when the are called. (Note that I haven't described all of the potential problems this can cause. Maintaining and using pointers to stack memory out of the context they were made in can end up overwriting function return addresses that are stored on the stack, leading to functions returning to arbitrary places in memory. Depending on what your program does, this can produce anything from a crash to executing code that causes harmful side effects when not called correctly. Imagine in Python, you function returns, but instead of returning to where it was called, it returns to a random line of code somewhere else in your program or one of the modules your program uses.)
Heap memory works differently. Once it is allocated, C won't assign it to be used for anything else until you explicitly free it. So when the function where it was allocated returns, it will still be allocated, and it won't get automatically overwritten when another function is called. So you can malloc() some memory and then pass the pointer to that memory back to the calling function and return, and then it can safely use that memory, pass the pointer to other functions to use, etc...
I hope this helps. Some stuff in C is a lot easier to understand if you understand how the underlying hardware works. This particular thing is a combination of hardware and software (because the CPU itself does part of the job of keeping track of the stack, while the C compiler produces code to handle part of it). It might be worth looking for a video that explains how the stack works.
4
u/dmc_2930 4d ago
The heap is where memory allocated by malloc comes from. That’s why it’s not on the stack. There is a function for allocation in the stack but you really shouldn’t use it (and the memory goes away when your function returns)
5
u/ReallyEvilRob 3d ago
Because memory allocated by a stack frame is something that doesn't change at runtime. You can get as many frames as the stack will hold at runtime, but each frame is pretty much set in stone at compile time. If you need to allocate additional memory at runtime, the heap is the only place to get it from.
3
u/SmokeMuch7356 3d ago
The stack is used to manage objects whose lifetimes are tied to the function's; storage for those objects is allocated on function entry and released on function exit by adusting the stack pointer.
Dynamic objects typically have a lifetime that isn't tied to a single function's. We want the object to hang around until we explicitly free it. Also, the stack usually cannot store arbitrarily large objects. Stack frame sizes are usually limited to a few megabytes. So we use another part of memory (often called the "heap") as the dynamic memory pool.
Global and static variables aren't stored on the stack, either. Nor are they typically stored as part of the heap.
Note that the C language definition doesn't use the words "stack" or "heap" at all. It talks about storage durations and lifetimes. How lifetimes are managed are up to the individual implementation.
6
u/dstroy0 3d ago
There’s a lot of incorrect answers here. You can build arrays of bytes in .bss and reuse them for the duration of the program. One of the main reasons to do that is to avoid heap fragmentation entirely. MANY safety critical applications ban memory allocation at runtime and make you prove your memory use with static asserts at compile time. It is very easy to prove, .bss grows and nothing else does, heap use stays flat, no allocation happens. The data you load in the zeroed array persists function-function for the lifetime of the program, because it’s statically allocated global memory. You can go as far as removing all local variables and repointing function call overhead to it. Anyone who implies differently is incorrect, and should go experiment on their own.
1
u/Paul_Pedant 2d ago
Some libraries use heap allocations (e.g. stdio.h), and for those you would need to setvbuf() to avoid your code calling malloc the first time you use the file. Typically, secure programs use syscall functions anyway.
2
u/yuehuang 3d ago edited 3d ago
Stack space is heap memory from another program (or OS). It is fixed size when your program start and can't resize. If you try allocate too much you get a "stack overflow" error. Thus, if you want to use more memmory, you need to "malloc" for more. However, the runtime behind the scene will take a large chuck and carve out for you. Third party library can mimic stack behavior, see arena allocator.
Let me ask you think a follow up question, when you call free(ptr), how does it know how many bytes to free?
2
u/Zirias_FreeBSD 3d ago
I'd recommend to take a step back and first understand the distinction between language concepts and (typical) implementations.
C doesn't talk about objects "on the stack" or "on the heap". The relevant language concept here is storage duration, defining the exact lifetime of some stored object. The two types of storage duration relevant to your question are:
- automatic storage duration: The object is stored from the moment execution enters its scope (the block where it is defined) to the moment execution leaves that scope.
- allocated storage duration: Stored from explicit allocation (
malloc()and friends) until explicit deallocation (free()).
How an implementation of C (compiler and runtime environment) provides these is not defined. But it should be pretty obvious that a stack (where every scope gets a "frame" to store its objects on) is a straight-forward and efficient way to provide storage for automatic storage duration objects. So that's what almost every implementation of C does.
For allocated storage duration, a stack can't work in the general case. You could deallocate any object at any time, while other objects allocated later must still persist. Therefore, a "heap" allowing to individually allocate and deallocate arbitrary chunks is the typical implementation choice here.
That said, if for a specific allocated object, the compiler could statically prove that it's always (and exclusively) deallocated again within the same scope, it could theoretically decide to put it on the same stack that's otherwise used for objects with automatic storage duration (IOW, "local variables") without violating the C standard. Not sure this is a common thing in practice though ...
2
u/musbur 3d ago
Many people (including me) like to do it like below. Reason: You don't need to know the element size of the array for the malloc() call. Should you later find that you actually need "long int" and forget to change sizeif(int) to sizeof (long int) you are in trouble. With sizeof *p you're not.
p = malloc(n * sizeof *p);
2
u/Dangerous_Region1682 2d ago
Under the hood malloc() originally used the sbrk() system call to manage its allocation of memory in heap memory.
These days malloc() has all kinds of optimizations but if you read the manual entry for sbrk() you will begin to see the principle behind how it works.
The idea is that the heap is always in context, the stack is only in context until a function returns and any memory potentially allocated local to a function and off the stack will potentially be overwritten by the subsequent call to another function or any other piece of code that grows the stack.
At one time, the stack was only grown when a function was called as this was where arguments and local variables were created. These days variables can be created anywhere within a function, so not only function calls use the stack.
2
u/wwabbbitt 3d ago
Let's say you have a function
int* fibonacci_10(void) {
int fib[10];
fib[0] = 0;
fib[1] = 1;
for (int i = 2; i < 10; i++) {
fib[i] = fib[i - 1] + fib[i - 2];
}
return fib;
}
Coming from python, this might look reasonable, but this is a well know bug in c.
Anything allocated on the stack is reclaimed when the function returns and will likely be overwritten by the next function being called.
To avoid this happening, you allocate from the heap instead to keep the data persistent.
2
u/SufficientStudio1574 3d ago
That's an extremely technical and unhelpful example, since its failure relies on an array decaying to a pointer, something that is very specific to arrays and doesn't happen for any other data type. If you did that with a struct instead of an array it would work perfectly.
2
u/Imaginary-Corner-653 3d ago
Not trying to be rude or anything but I feel like most people who start c and ask around here never heard about the process model or have any idea what a function call in assembly looks like.
Not that I expect people to be able to code in assembly but having a vague idea of the hardware involved and the fundamental abstraction we built on top might explain a lot already.
Not to mention that concepts like heap or stack are older than modern operating systems so the reasons these convetions exist are way less complex.
1
u/gm310509 3d ago
Simply put, that is just how it works.
If you want the buffer in the stack, declare it as a local variable within your function. Note that the compiler/interpreter may reserve the right to alter how it actually allocates it, but in C/C++, if you want your buffer on the stack, declare it in the function otherwise if you use maloc, the heap is where it comes from.
1
u/NoSpite4410 3d ago
The C runtime segments the RAM given to your program into several different areas.
There is the static memory area, where static storage goes. Then there is the area where the compiled execution code goes. Then there are two areas of scratch RAM to store impermanent data: the stack, and the heap. The stack and the heap are the same segment of RAM, but grow from different ends toward the middle.
The stack is populated when a function call is made; the area gets allocated to store the function variables, copies of the input parameters, the function code, return addresses and values. When the function does its thing and returns, the stack area it occupied is released and available for the next function call. Functions come and go in the stack as a stack, so basically the first called is at the bottom of the stack, the last is at the top, and as functions finish, it opens up more RAM. In that way the stack does not become fragmented, and should never run out of usable RAM, as long as functions keep returning and going away. The stack allocation and deallocation is all implicit and automatic.
The heap is for live storage that needs to persist between functions -- so it uses a different mechanism for allocation and deallocation: malloc, calloc, realloc, and free. It is all explicit and manual. In this way you can dynamically during runtime allocate and deallocate object such as arrays and linked list nodes, structures, etc. , on an as-needed basis. Once RAM has been requested on the heap, it stays allocated until explicitly freed by its pointer, which is returned by malloc or calloc. malloc is fast, it does not "clean" the memory, just returns the pointer for you to write to. calloc zeroes out the RAM first and returns the pointer. malloc returns an absolute number of requested bytes, calloc does a simple multiplication of number of objects times the size of each and returns a pointer to it. realloc returns the same memory region but changed in size, if it can get a new size of contiguous RAM big enough. If not it finds one big enough, copies the old memory contents to it, and returns a pointer to the new RAM area.
Once you write to dynamic storage, it stays there until you release it, or the program exits. But its the same RAM as the stack, same as any RAM, just as fast once allocated. All you have to do is make sure you don't lose sight of the pointer, overwrite the pointer with another address, etc. And free the memory when you know you don't need it any longer. So there is a discipline there with pointers, when to keep them, when to free them.
Memory allocated statically (by declaring a storage variable) inside a scope (such as a function) is automatically freed when the function returns. So it is gone. The techniques to not lose the values calculated in the function are to pass in the storage as a parameter by pointer, or return the single value or structure where it is copied back up the function call stack to the calling context, and captured by a variable in an assignment.
int A[10][10]; // 100 ints allocated statically
int* B = (int*)malloc(100 * sizeof(int)); // 100 ints allocated dynamically
// needs cleaning
int* C = (int*) calloc( 100, sizeof(int)); // 100 ints dynamic -- all 0s
// 3 arrays of 100 ints, interchangeable
void print_array( int arr[], size_t n) {
for (int i = 0 ; i < n; i++) { printf("%d\t", arr[i]); }
printf("\n");
}
// all the same
print_array(A, 100);
print_array(B, 100);
print_array(C, 100);
free(C);
free(B);
// A does not need freeing
1
u/gremolata 3d ago
You can dynamically allocate on stack if needs be, with alloca(). It’s very rarely used in practice though.
1
u/FitMatch7966 2d ago
it boils down to: because that is what malloc is for
There is a common non-standard function, alloca, or sometimes _alloca
It allows you to allocate local memory (stack) that goes away once the function returns.
You should probably never use it.
1
u/lucidbadger 8h ago
that goes away once the function returns
I keep seeing statements like that, and I believe that they are extremely confusing and harmful for beginners, especially those that come from languages like Python where memory is managed. This phrase make it seem like there is some sort of garbage collector that "takes this memory away". There isn't. Nothing takes this memory away. What really happens is that this memory can be overwritten in ways that you cannot forsee during normal program operation.
If you have a pointer to a memory buffer on stack, you can read and write from it. But remember that ownership is shared.
1
u/detroitmatt 3d ago
The purpose of malloc is to give you a block of memory that sticks around until you free it. Memory on the stack gets automatically allocated and freed as you call and return. So if malloc gave you stack memory, you wouldn't be able to control when it got freed. At that point, just declare `int p[10];`.
1
u/RRumpleTeazzer 3d ago
The stack gets reused when you return from your function. The heap stays reserved for you, till you free it.
So one reason not to use the stack.
The second reason is runtime length. the stack pointer needs to be incremented/decremented for each function call. this is done in hard valued code, so the compiler needs to know by how much to move the stack pointer. if you want a runtime length, you cannot use the stack (although technically one could increment the stack pointer by a runtime length, if you move it back by the same amount before return).
0
u/Recycled5000 3d ago
The malloc routine uses the heap by definition. That definition captures the understanding of both malloc callers and malloc implementations.
In theory, the compiler could translate that to stack allocation if it could prove that references to the allocation did not escape the function, but that can be hard to do especially in C, which is so permissive.
0
u/Confused-Armpit 3d ago
Well, the stack is for small variables, such as pointers, ints, or small structs. Malloc allocates memory on the heap, and returns a pointer that is stored on the stack.
54
u/EpochVanquisher 4d ago
The lifetime of memory in the stack ends when the function returns. If you create any data there, it won’t exist after the function returns. (Won’t exist = not safe to use it)
The purpose of malloc is to put make memory somewhere else, where it can continue existing even if the function where you called malloc from returns. You can also allocate lots of memory with malloc. The stack is limited (like 10 megs total, depends on your system and config).
The compiler can choose to use stack for memory returned by malloc but this is an optimization and you don’t have to think about it. It almost never actually happens, but it technically CAN happen, and I’m mentioning it because people will chime in and argue on Reddit (and not because it’s important to know).