Does this mean the base pointer or the stack pointer are actually moving down the memory addresses instead of going up? Why is that?
Yes, the push
instructions decrement the stack pointer and write to the stack, while the pop
do the reverse, read from the stack and increment the stack pointer.
This is somewhat historical in that for machines with limited memory, the stack was placed high and grown downwards, while the heap was placed low and grown upwards. There is only one gap of "free memory" — between the heap & stack, and this gap is shared, either one can grow into the gap as individually needed. Thus, the program only runs out of memory when the stack and heap collide leaving no free memory.
If the stack and heap both grow in the same direction, then there are two gaps, and the stack cannot really grow into the heap's gap (the vice versa is also problematic).
Originally, processors had no dedicated stack handling instructions. However, as stack support was added to the hardware, it took on this pattern of growing downward, and processors still follow this pattern today.
One could argue that on a 64-bit machine there is sufficient address space to allow multiple gaps — and as evidence, multiple gaps are necessarily the case when a process has multiple threads. Though this is not sufficient motivation to change things around, since with multiple gap systems, the growth direction is arguably arbitrary, so tradition/compatibility tips the scale.
You'd have to change the CPU stack handling instructions in order to change the direction of the stack, or else give up on use of the dedicated pushing & popping instructions (e.g. push
, pop
, call
, ret
, others).
Note that the MIPS instruction set architecture does not have dedicated push
& pop
, so it is practical to grow the stack in either direction — you still might want a one-gap memory layout for a single thread process, but could grow the stack upwards and the heap downwards. If you did that, however, some C varargs code might require adjustment in source or in under-the-hood parameter passing.
(In fact, since there is no dedicated stack handling on MIPS, we could use pre or post increment or pre or post decrement for pushing onto the stack as long as we used the exact reverse for popping off the stack, and also assuming that the operating system respects the chosen stack usage model. Indeed, in some embedded systems and some educational systems, the MIPS stack is grown upwards.)
We refer to multi-byte items by the lowest address among them — i.e. by their first byte aka the beginning. Another advantage of growing the stack downward is that, after pushing, the stack pointer refers to the item recently pushed onto the stack, no matter its size. Growing the stack in the reverse direction means pointing to the logical end of the last item pushed.