Yes Tater every dynamic allocation is relative addressed with reference to base stack pointer which is EBP.
If you take a look to assembler generated you will see a lot of move in some register the value from location pointed by EBP plus an offset like "mov eax, [EBP+0x12]".
That's why you have a prolog where you save the actual stack pointer in EBP, that will be base frame, than some value will be added to stack pointer to create space on the stack. Because for IAPX architecture the stack grows toward lower addresses, we must *subtract* from stack pointer the space we need. The subtracted value must leave the stack aligned on natural size (on DWORD boundary for 32bits and QWORD boundary for 64). This way anything that will be pushed expressily (like a push instruction), or implicitly (like the return address of a subroutine call) will lay beyond that point.
The value added to the stack pointer is the space required to store local or better dynamic variables. The parameter passed to the function are above (for IAPX) the base pointer EBP, so tipically you access local variables with positive offsets referred to base pointer and negative offsets for parameters.
The function alloca() can squeeze or enlarge the local variables area (between EBP and actual stack pointer ESP) in some cases when the requested space could not be determined statically during compilation (the compiler calculates the amount required for local variables and hardcodes the value to add to stack in prologue) is dinamically chnged by alloca().
The use of this function largely discouraged for the problems that can cause considering it dangerous (this is common opinion around), but the real reason is that the abuse of such a practice generally lead to stack overflow problems.
From what you said the stack overflow could be the culprite, anyway I suggest to enlarge stack at least 4M. Consider that the stack can grow by itself, the OS should dinamically increase it on request, but because it have to cause a memory exception to trigger the virtual memory manager to allocate more data, maybe that on very large structures your access is much far away leading to memory violation before memory reallocation. This means that the standard 4k (1 page) of reallocation could be insufficient (is the second parameter of /STACK switch), so maybe you want allocate 4 or even 8 pages at time.
This depend on your code, consider that enlarging the stack will erode memory resources that will remain allocated in your program, enlarging the allocation size could fit the request determining almost automatically the memory you need.
Last consider that the stack memory on program entry is allocated, but not committed. This means that the memory manager take a note of how much memory you require, but the real allocation, committment, is done when you *access* memory (or try to do it).
JJ yes, the creation of stack frame is *only* for functions. The scoping determine the visibility of a variable, but the memory allocation is the same along the whole function. Using optimizations the compiler could reuse memory, but this is not limiting it can do whatever it considers usefull to reduce resources (CPU, memory, time).
EDIT: I correct some incongruences, and attach an image to better explain the mechanism.
For 64bits the thing is a little bit more complex due to the calling convention (__fastcall) used, but the base working of stack handling remains the same.