In Win32 processes, the stacks and heaps are distributed somewhat erratically throughout the address space. x86 apps on Windows XP and higher typically have a layout like this:
0x001x0000: First thread's stack, typically has a meg of initial size and grows towards negative addresses if it exceeds that space (the 'top' page of the stack is marked with a guard page which causes additional virtual memory allocation to occur when it's accessed).
0x00400000: EXE's typical load address.
Everything else is distributed fairly randomly throughout process space; DLLs, additional thread stacks, memory mapped files, thread-local storage blocks, heaps, etc.
When Windows grows a heap, it can allocate the new pages anywhere so that fragmentation doesn't cause out-of-memory errors as easily (heaps are not one large contiguous set of pages). When Win32 stacks grow, they cause stack overflow exceptions if they would collide with any existing pages (whether it's a heap, the EXE, a DLL, or another stack - running into ANYTHING causes the stack overflow exception). Stacks are typically preallocated with as much space as they need so that they can be allocated as one big block and (ideally) not need to grow any further. The Win32 API lets you control the initial stack size for your EXE and when you make any new threads for this reason.
The important part to understand is that the language runtime and OS determine how the stacks and heaps behave, so this can be COMPLETELY different on other platforms like Linux or iOS.