You can basically visualize the stack however you like. The more often encountered way to represent stack is that a stack is said to grow down, from higher addresses to lower addresses.
To support this narrative, stack is often visualized as
+------------------+ <- top of memory = 0x10000
| used_stack |
| |
+------------------+
| local variables | <- top of stack = 0xff00
| [red zone] | <- red zone is guaranteed to be unused
| unused memory | by exception handlers
| |
| ---------------- |
| HEAP | <- dynamically allocated memory
+------------------+ address = 0x0000
Personally I found this unintuitive, since this reverses the order in which other memory structures or arrays are typically presented. There's no down in a CPU.
| offset description. hex dump
+-----------+----------------+------------------------
| 0 | char header[4] | 01 02 03 04
| 8 | int16 width | 08 00
| 10 | int16 height | 04 00
| 12 | int32 size | 20 00 00 00
vs.
struct FileFormat {
char header[4];
int16_t width{8};
int16_t height{4};
int32_t size{32};
};
When stack is visualized as a stalactite (hangs like an icicle) it physically grows down, but it loses the connection to real life stacks, as in stack of plates or books, which grow up.
A B
+-------------+ +--------------+
| ceil of stk | <-high addresses | unused | <-small addresses
| 20 00 00 00 | | |
| 08 00 04 00 | | |
| 01 02 03 04 | <-- top of stack -> | 01 02 03 04 |
| | | 08 00 04 00 |
| | | 20 00 00 00 |
| unused/heap | |bottom of stck|
+-------------+ +--------------+
The format A is more convenient at least when you have a written text like
push 1
push 2
push 3
In the style A, this is represented as is, in style B, the values are reversed.