I don't think the way you are using this term is common. Most of the time when people refer to "the heap" of a program, they mean the area(s) from which dynamic memory is allocated (malloc()/calloc()/realloc() in C with new/delete added in C++).
Your variable named "heap" is static and uninitialized, so it will commonly be stored in a "BSS" area which is allocated and fixed at program startup time. Dynamically allocated memory can be (but does not need to be) obtained using (s)brk(), which is a legacy way that indeed implies a block area of dynamic memory. However, the more modern mmap() interface to obtain memory may behave in a completely different manner and grab pages from varying distinct locations (one obvious newfangled justficiation comes to mind immediately: address space randomization), and that's what many modern malloc() implementations use instead of (s)brk().
Also, the issue is even less clear-cut in multithreaded applications - which have more than one stack.
Quote:
stacks down from the virtual address range ceiling
Counter example: HP-UX, where the stack "grows upwards".
To summarize, address space layout is strongly system-specific and it is rarely possible, meaningful or useful to generalize its characteristics.
What point were you trying to make?