This study material is compiled from various sources, including a lecture audio transcript and copy-pasted text, to provide a comprehensive overview of "Names, Bindings, and Scopes" in programming languages.
📚 Chapter 5: Names, Bindings, and Scopes
🎯 Introduction
This chapter explores fundamental concepts in programming languages related to how program entities are identified, associated with attributes, and how their visibility is managed. Understanding names, bindings, and scopes is crucial for writing correct, readable, and maintainable code. Imperative programming languages are often abstractions of the von Neumann architecture, which relies on memory and a processor executing instructions via a fetch-decode-execute cycle. This architecture necessitates mechanisms for naming and managing data and operations.
🏷️ Names and Identifiers
What Needs a Name?
Many elements within a program require a name for identification and reference. These include:
- Constants, variables
- Operators
- Statement labels
- Types
- Procedures, functions, methods
- Modules, programs
- Files, disks
- Commands, menu items
- External entities like computers, networks, and user login names
Definition of Names and Identifiers
A name is formally denoted by an identifier.
- Typical Identifiers: Usually constructed from letters, digits, and underscores.
- Unconventional Identifiers: Some languages (e.g., Prolog, Scheme, Perl) allow more unusual characters.
Design Issues for Names
Several factors influence the design and usage of names in programming languages:
-
Length 📏
- If names are too short, they may not be descriptive (connotative).
- Many modern languages (C#, Java, C++, Python) have no official length limit, though implementers might impose practical limits. All characters are typically significant.
-
Special Characters 💲
- Some languages require specific characters in name definitions.
- PHP: All variable names must start with a dollar sign (
$). - Perl: Variable names begin with special characters indicating their type.
- Ruby:
@for instance variables,@@for class variables.
-
Case Sensitivity 🔡
- Case-Sensitive Languages:
Sumandsumare considered different names (e.g., C-based languages, Python).- ⚠️ Disadvantage: Can hinder readability as visually similar names are distinct. This is further complicated in C++, Java, and C# where predefined names often use mixed case (e.g.,
IndexOutOfBoundsException).
- ⚠️ Disadvantage: Can hinder readability as visually similar names are distinct. This is further complicated in C++, Java, and C# where predefined names often use mixed case (e.g.,
- Case-Insensitive Languages:
Sumandsumare the same name (e.g., Fortran, COBOL, Lisp).
- Case-Sensitive Languages:
-
Special Words ✨ Special words enhance readability by delimiting or separating statement clauses.
- Keyword: A word that is special only in certain contexts.
- Reserved Word: A special word that cannot be used as a user-defined name.
- ⚠️ Potential Problem: Too many reserved words can lead to naming collisions (e.g., COBOL has ~300 reserved words).
📊 Variables
A variable is an abstraction of a memory cell. Variables are characterized by a sextuple of attributes:
-
Name: The identifier used to refer to the variable.
- Unnamed Variables: The concept of "unnamed variables" often refers to conventions like using a single underscore (
_) for throwaway variables whose value isn't needed (e.g.,name, _ = ("John Doe", 35)).
- Unnamed Variables: The concept of "unnamed variables" often refers to conventions like using a single underscore (
-
Address: The memory location associated with the variable.
- A variable might have different addresses at different times or places in a program.
- Aliases: When two variable names refer to the same memory location. Created via pointers, reference variables, or C/C++ unions.
- ⚠️ Harmful to Readability: Programmers must track all names referring to the same location.
-
Type: Determines the range of values a variable can hold and the set of operations defined for those values. For floating-point types, it also determines precision.
-
Value: The content stored in the memory location associated with the variable.
- l-value (left-value): Refers to the address of a variable.
- r-value (right-value): Refers to the value of a variable.
- Example: In
x = y;- The l-value is the address of
x. - The r-value is the value of
y.
- The l-value is the address of
- For array elements like
T[i*2+1] = y;, the address (l-value) depends on the current value ofi.
-
Lifetime: The duration during which a variable is bound to a particular memory cell.
-
Scope: The range of statements over which a variable is visible.
🔗 The Concept of Binding
A binding is an association between an entity and an attribute (e.g., a variable and its type or value, or an operation and a symbol).
Binding Time
Binding time is the moment when a binding takes place.
- Language Design Time: Operator symbols bound to operations (e.g.,
+to addition). - Language Implementation Time: Floating-point types bound to a representation (e.g., 32-bit float).
- Compile Time: Variable types, storage classes, or fixed sizes bound in statically typed languages (e.g., C/Java).
- Link Time: Function calls bound to specific code definitions in libraries.
- Load Time: Static or global variables bound to physical memory addresses by the OS.
- Runtime: Local stack variables bound to memory locations, or values assigned to variables during execution.
Static vs. Dynamic Binding
- Static Binding: Occurs before runtime and remains unchanged throughout program execution.
- Dynamic Binding: Occurs or can change during program execution.
📝 Type Binding
Type binding addresses how a variable's type is specified and when this binding occurs.
Explicit and Implicit Declaration
- Explicit Declaration: A program statement used to declare variable types.
- Example:
int a;
- Example:
- Implicit Declaration: A default mechanism for specifying types through conventions, often via type inferencing based on context.
- Example:
num = 10 # num automatically becomes an integer name = "Alice" # name automatically becomes a string - Advantage: Improved writability (less code).
- ⚠️ Disadvantage: Reduced reliability; prevents compilation from detecting typographical errors, potentially assigning default types and unexpected attributes to undeclared variables.
- Example:
Dynamic Type Binding
In languages like JavaScript, Python, Ruby, PHP, and C# (limited), types are specified through assignment statements.
- Example:
list = [2, 4.33, 6, 8]; // list is an array list = 17.3; // list is now a float - Advantage: Flexibility, allowing generic program units.
- ⚠️ Disadvantages:
- High cost due to dynamic type checking and interpretation.
- Difficult for compilers to detect type errors.
💾 Storage Binding and Lifetime
- Allocation: Getting a memory cell from a pool of available cells.
- Deallocation: Returning a memory cell to the pool.
- The lifetime of a variable is the time during which it is bound to a particular memory cell.
Variables are categorized into four types based on their storage binding lifetime:
-
Static Variables ✅
- Bound to memory cells before execution begins and remain bound throughout execution.
- Example: C and C++
staticvariables within functions. - Advantages: Efficiency (direct addressing), support for history-sensitive subprograms (retain values between calls).
- ⚠️ Disadvantage: Lack of flexibility (no recursion).
-
Stack-Dynamic Variables ✅
- Storage bindings are created when their declaration statements are elaborated (executed).
- If scalar, all attributes except address are statically bound.
- Example: Local variables in C subprograms and Java methods.
- Advantages: Allows recursion, conserves storage.
- ⚠️ Disadvantages: Overhead of allocation/deallocation, subprograms cannot be history-sensitive, inefficient references (indirect addressing).
-
Explicit Heap-Dynamic Variables ✅
- Allocated and deallocated by explicit programmer directives during execution.
- Referenced only through pointers or references.
- Example: Dynamic objects in C++ (via
newanddelete), all objects in Java. - Advantage: Provides dynamic storage management, useful for structures like trees and lists that grow/shrink.
- ⚠️ Disadvantage: Inefficient and potentially unreliable (e.g., memory leaks if not deallocated).
-
Implicit Heap-Dynamic Variables ✅
- Lifetime determined at runtime; starts when a value is assigned (on the heap) and ends when no longer referenced (deallocated by garbage collector).
- Example: All variables in Python and PHP.
x = 10 # 'x' bound to an integer object on the heap x = "hello" # 'x' now bound to a string object; previous int object deallocated if unused - Advantage: Flexibility (generic code).
- ⚠️ Disadvantages: Inefficient (all attributes are dynamic), loss of error detection.
🌐 Scope
The scope of a variable is the range of statements over which it is visible.
- Local Variables: Declared within a specific program unit.
- Nonlocal Variables: Visible in a unit but not declared there.
- Global Variables: A special category of nonlocal variables, visible throughout the program.
- Scope Rules: Determine how references to names are associated with variables.
Nonlocal Variable Example (Python)
def outer_function():
count = 0 # This is a nonlocal variable to inner_function
def inner_function():
nonlocal count # Declare 'count' as nonlocal to modify the outer variable
count += 1
print("Inner count:", count)
inner_function()
print("Outer count:", count)
outer_function()
# Output:
# Inner count: 1
# Outer count: 1
Static Scope (Lexical Scope)
Determines a variable's visibility based on its location within the source code, fixed at compile-time.
- Lexical Structure: Scope defined by where the variable is declared (e.g., within
{}or functions). - Compile-Time Determination: Easier to debug and optimize.
- Scope Resolution Order: Compiler searches in this order: Current Block → Enclosing Blocks → Global Scope.
- Local Static Variables: Declared inside a function, visible only within that block, but retain their value between function calls (initialized once).
- File/Global Static Variables: Restricted to the specific file (translation unit) where declared, preventing access from other files.
- Example (C):
// File1.c static int file_scoped_var = 10; // Only visible in file1.c int global_var = 20; // Visible in the entire program // File2.c extern int global_var; // Declares global_var is defined elsewhere // Attempting to access file_scoped_var here would cause an error
- Example (C):
- Most modern languages (C, C++, Java, Python) use static scoping.
Dynamic Scoping
The value of a variable is determined by the most recent function call in the program's call stack.
- Search Order: Current function → Calling function → Functions higher in the call stack.
- Disadvantages:
- Difficult to understand at compile time which variables refer to which objects.
- Type checking is impossible at compile time.
- Rarely used in modern programming languages.
Static vs. Dynamic Scoping Example 💡
Consider the following pseudocode:
main P() {
int X;
void A() {
X = X + 1;
print(X);
}
void B() {
int X;
X = 17;
A();
}
X = 23;
B();
}
-
main PcallsB, thenBcallsA. WhenAprintsX, whichXis it?-
With Static Scoping: The
XinArefers to theXinP(its enclosing block).P'sXis initialized to23.Bis called.B's localXis17, butAdoesn't see it.AincrementsP'sX(23 + 1 = 24).- Output:
24
-
With Dynamic Scoping: The
XinArefers to theXinB(the most recently entered block with anXdeclaration in the call chain).P'sXis initialized to23.Bis called.B's localXis17.Ais called fromB.AincrementsB'sX(17 + 1 = 18).- Output:
18
-
🗺️ Referencing Environments
The referencing environment of a statement is the collection of all names visible in that statement.
- Static-Scoped Language: Local variables plus all visible variables in all enclosing scopes.
- Dynamic-Scoped Language: Local variables plus all visible variables in all active subprograms.
- A subprogram is active if its execution has begun but has not yet terminated.
🔢 Named Constants
A named constant is a variable that is bound to a value only when it is bound to storage.
- Advantages: Improved readability and modifiability (parameterizing programs).
- Binding of Values:
- Static (Manifest Constants): Bound at compile time.
- Dynamic: Bound at runtime.
- Language Examples:
- C++ and Java: Named constants can be expressions of any kind, dynamically bound.
- C#: Has
readonly(dynamically bound) andconst(compile-time bound) named constants.
📝 Summary
This chapter covered the essential concepts of names, bindings, and scopes in programming languages.
- Names are identifiers with design considerations like length, case sensitivity, and special words.
- Variables are abstractions of memory cells, characterized by six attributes: name, address, value, type, lifetime, and scope.
- Binding is the association of attributes with program entities, occurring at various binding times.
- Type Binding can be explicit, implicit, or dynamic, each with trade-offs between writability, reliability, and flexibility.
- Storage Binding defines a variable's lifetime, categorizing variables as static, stack-dynamic, explicit heap-dynamic, or implicit heap-dynamic.
- Scope determines a variable's visibility. Static (lexical) scoping is compile-time determined and widely used, while dynamic scoping is runtime-determined and less common.
- Referencing environments define the set of visible names at any given point.
- Named constants enhance readability and modifiability.








