Programs written in the procedural style (as opposed to object-oriented style) are organized as a logical hierarchy of subroutine calls. In general, each subroutine call involves passing arguments from the caller to the callee. In addition, the callee may declare temporary local variables. Subroutine arguments and automatic local variables are accommodated at the top of virtual memory in an area known as the stack segment or simply as the stack. See Figure 5.
The hierarchy of subroutine calls begins when the operating system invokes the program’s main() function in C or the MAIN program in Fortran. Under normal circumstances, it ends when “main” returns to the operating system. The entire sequence can be represented as a call graph like that in Figure 6.
- OS calls main
- main calls func1
- func1 calls func2
- func2 returns to func1
- func1 calls func3
- func3 returns to func1
- func1 returns to main
- main calls func4
- func4 returns to main
- main returns (exit status) to OS
Fig. 6 Typical subroutine call graph.
Before calling main, the operating system pushes the elements of the command line that was used to invoke the program on “top” of the initially empty stack. In C, the main() function has access to these arguments through the argc and argv parameters, while Fortran MAIN programs can use the IARGC and GETARG subroutines, which are non-standard extensions.
As execution commences, main pushes its automatic variables on top of the stack. This makes the stack “grow” towards lower addresses. Then, just prior to calling func1, main pushes the arguments to func1. Together, main’s automatic variables and the arguments to func1 constitute a stack frame. Stack frames accumulate on the stack as the program descends the call graph, and are dismantled as it ascends. The procedure is outlined in Figure 7 below.
By convention, the currently active subroutine can only reference the arguments it was passed and its own local automatic and static variables (plus any globally accessible data). For example, while func2 is executing, it can not access func1′s local variables, unless of course func1 passes references to its local variables in the argument list to func2.