Compiler Design
Symbol Table / Scope Management in Detail
Scope Management
Scope is the structural region of a program within which an identifier's declaration is valid and accessible.
Lexical vs Dynamic Scope
The scope of an identifier is determined entirely by the textual (source code) structure of the program at compile time.
- Modern languages (like C, C++, Java, Python) use lexical scoping.
- Easy to reason about because scopes are static and visible directly in the code editor.
The scope is resolved at runtime based on the execution call stack (i.e., which procedure called which, rather than where the variables are declared).
- Used historically in early versions of Lisp, Perl, and APL.
- Can make debugging extremely challenging as variables can change based on the call sequence.
Block Scoping & Stack of Symbol Tables
For block-structured languages supporting nested blocks (like C/C++ or Java), the compiler manages scopes using a **Stack of Symbol Tables**. Entering a block pushes a new table, and exiting a block pops the top table.
Consider the following C code snippet:
int a = 1; // Global scope (table 0): { a: int }
void f(int x) { // ENTER SCOPE -> PUSH table 1: { x: int }
float a = 3.1; // table 1: { x: int, a: float } -- shadows global 'a'
{ // ENTER SCOPE -> PUSH table 2
int b = 9; // table 2: { b: int }
int c = a; // lookup(a) -> checks table 2 (no), checks table 1 (YES: float 'a')
} // EXIT SCOPE -> POP table 2
} // EXIT SCOPE -> POP table 1Lookups in Scope Stack:
When the compiler encounters int c = a; in the innermost block:
- It looks up
ain the top table (Table 2). It's not found. - It checks the parent scope table (Table 1). It finds
float a = 3.1;. - The compiler resolves
aas the local float parameter, shadowing the global integera.
Scope Rules in C
C features four primary scope structures:
- File Scope: Declared outside functions. Visible from the declaration point to the end of the file.
- Block Scope: Declared inside
{ ... }. Visible only until the closing brace. - Function Scope: Labels (like targets of
goto) are the only identifiers with function scope, meaning they can be jumped to from anywhere in the same function. - Prototype Scope: Parameter names in function declarations (e.g.
void f(int width, int height);) are only scoped within the declaration itself.