Stack backwards ... head hurts...
Oh my... I know there are numerous reasons why the stack has to be stored backwards, but boy, my head does hurt from trying to wrap my feeble brain around this...
I try to calculate the EBP-relative offset at which my variable is stored, and I'm off by 4 bytes (one variable).
Why? Well, because since the stack is stored backwards, I have to reference the "end" of the variable. I.e. the number at the right of each of these boxes is the one to subtract from EBP to access it, not the one on the left which one would expect.
That's what you get for having too visual a memory and then drawing a diagram with numbers decreasing towards the right ... pfft!
Similarly, it took me quite a while to realize why I was supposed to push structs on the stack with their fields in reverse order. It wasn't reverse order. It was exactly the order needed to have them maintain their correct order, where field 2 has a higher address than field 1...
Nothing better than a new programming subject to make one realize how little one really knows. Not to mention that half of this I had to be pointed at by assembler professionals on a public mailing list ... nothing better than public humiliation to get one's brain back into working order.
Interestingly (at least from a historic perspective), the stack growing backwards is the reason that the order C evaluates arguments to a function is undefined. See http://cm.bell-labs.com/cm/cs/who/dmr/clcs.html specifically the section titled "Effects on the Language" for words from the language designer himself.
Consider varargs functions, those with an indeterminate number of arguments. Consider printf() - it can be quite sure that its first argument is at SP-4 irrespective of how many args were passed.
Many years back, I worked on a PRIME minicomputer which did not have a contiguous stack - instead, each function was responsible for allocating a block of "scratch memory" which it had to pass in to any function it called - this made it perfect for doing "stack crawlbacks" but a nightmare for compiler writers since the compiler had to determine (and record) the amount of temporary stack space a function would need, and to CALL a function, you needed to look at its declared requirement. Little surprise that the natural language on a PRIME was Fortran - to implement C, they actually had to code knowledge of printf()s behaviour into the compiler.
Still, that was better than GCOS with its 9 bit bytes. :-)
|Uli Kusterer replies: ★|
@Jeff: Yeah. I was quite happy to find out that, since on OS X the *caller* cleans up the stack, you could easily add new parameters to a function which older versions would quietly ignore.
And 9-bit bytes: I'd heard that stuff like that existed, but so far I've never used a computer that had such an odd (from today's viewpoint) set-up. At least, not knowingly ;-)