auto int almond;
static int hazel;
instead of:
register int filbert;
int almond;
static int hazel;
A Stack Frame Might Not Be on the Stack
Although we talk about a "stack frame" being "pushed on the stack," an activation record need not be
on the stack. It's actually faster and better to keep as much as possible of the activation record in
registers. The SPARC architecture takes this to the limit with a concept called "register windows" in
which the chip has a set of registers solely dedicated to holding parameters in procedure activation
records. Empty stack frames are still pushed for each call; if the call chain goes so deep that you run
out of register windows, the registers are reclaimed by spilling them to the frame space already
reserved on the stack.
Some languages, such as Xerox PARC's Mesa and Cedar, have activation records allocated as linked
lists in the heap. Activation records for recursive procedures were also heap-allocated for the first PL/I
implementations (leading to criticism of slow performance, because the stack is usually a much faster
place from which to acquire memory).
Threads of Control
It should now be clear how different threads of control (i.e., what used to be called "lightweight
processes") can be supported within a process. Simply have a different stack dedicated to each thread
of control. If a thread calls foo(), which calls bar(), which calls baz(), while the main program is
executing other routines, each needs a stack of its own to keep track of where each is. Each thread gets
a stack of 1Mb (grown as needed) and a page of red zone betweeen each thread's stack. Threads are a
very powerful programming paradigm, which provide a performance advantage even on a
uniprocessor. However, this is a book on C, not on threads; you can and should seek out more details
on threads.
setjmp and longjmp
We can also mention what setjmp() and longjmp() do, since they are implemented by
manipulating activation records. Many novice programmers do not know about this powerful
mechanism, since it's a feature unique to C. They partially compensate for C's limited branching
ability, and they work in pairs like this:
- setjmp(jmp_buf j) must be called first. It says use the variable j to remember where
you are now. Return 0 from the call. - longjmp(jmp_buf j,int i) can then be called. It says go back to the place that
the j is remembering. Make it look like you're returning from the original setjmp(), but
return the value of i so the code can tell when you actually got back here via longjmp().
Phew!