Names, Bindings, Type Checking, and Scopes: Chapter 5 Topics
Names, Bindings, Type Checking, and Scopes: Chapter 5 Topics
Chapter 5 Topics
• Introduction
• Names
• Variables
• The Concept of Binding
• Type Checking
• Strong Typing
• Scope
• Scope and Lifetime
• Referencing Environments
• Named Constants
• Variable Initialization
Chapter 5
Names, Bindings, Type Checking, and Scopes
Introduction
• Imperative languages are abstractions of von Neumann architecture
– Memory: stores both instructions and data
– Processor: provides operations for modifying the contents of
memory
• Variables characterized by attributes
– Type: to design, must consider scope, lifetime, type checking,
initialization, and type compatibility
Names
Design issues for names:
• Maximum length?
• Are connector characters allowed?
• Are names case sensitive?
• Are special words reserved words or keywords?
Name Forms
A name is a string of characters used to identify some entity in a program.
If too short, they cannot be connotative
Language examples:
• FORTRAN I: maximum 6
• COBOL: maximum 30
• FORTRAN 90 and ANSI C: maximum 31
• Ada and Java: no limit, and all are significant
• C++: no limit, but implementers often impose a length limitation
because they do not want the symbol table in which identifiers are
stored during compilation to be too large and also to simplify the
maintenance of that table.
Names in most programming languages have the same form: a letter
followed by a string consisting of letters, digits, and (_).
Although the use of the _ was widely used in the 70s and 80s, that
practice is far less popular.
C-based languages (C, C++, Java, and C#), replaced the _ by the “camel”
notation, as in myStack.
Prior to Fortran 90, the following two names are equivalent:
• Case sensitivity
– Disadvantage: readability (names that look alike are different)
• worse in C++ and Java because predefined names are
mixed case (e.g. IndexOutOfBoundsException)
• In C, however, exclusive use of lowercase for names.
– C, C++, and Java names are case sensitive Î rose, Rose, ROSE
are distinct names “What about Readability”
Special words
• An aid to readability; used to delimit or separate statement clauses
• A keyword is a word that is special only in certain contexts.
• Ex: Fortran
Name
- Not all variables have names: Anonymous, heap-dynamic variables
Address
• The memory address with which it is associated
• A variable name may have different addresses at different places and at
different times during execution.
// sum in sub1
• A binding is static if it first occurs before run time and remains unchanged
throughout program execution.
• A binding is dynamic if it first occurs during execution or can change
during execution of the program.
Type Bindings
• If static, the type may be specified by either an explicit or an implicit
declaration.
Variable Declarations
i, x Î Integer
y Î floating-point array
i = xÎ what the user meant to type
i = yÎ what the user typed instead
fun times10(x) = 10 * x;
The argument and functional value are inferred to
be int.
Storage Bindings & Lifetime
– Allocation - getting a cell from some pool of available cells.
– Deallocation - putting a cell back into the pool.
– The lifetime of a variable is the time during which it is bound to a
particular memory cell. So the lifetime of a var begins when it is
bound to a specific cell and ends when it is unbound from that cell.
– Categories of variables by lifetimes: static, stack-dynamic,
explicit heap-dynamic, and implicit heap-dynamic
Static Variables:
– bound to memory cells before execution begins and remains bound
to the same memory cell throughout execution.
– e.g. all FORTRAN 77 variables, C static variables.
– Advantages:
• Efficiency: (direct addressing): All addressing of static vars
can be direct. No run-time overhead is incurred for allocating
and deallocating vars.
• History-sensitive: have vars retain their values between
separate executions of the subprogram.
– Disadvantage:
• Storage cannot be shared among variables.
• Ex: if two large arrays are used by two subprograms, which
are never active at the same time, they cannot share the
same storage for their arrays.
Stack-dynamic Variables:
– Storage bindings are created for variables when their declaration
statements are elaborated, but whose types are statically bound.
– Elaboration of such a declaration refers to the storage allocation
and binding process indicated by the declaration, which takes place
when execution reaches the code to which the declaration is
attached.
– Ex:
• The variable declarations that appear at the beginning of a
Java method are elaborated when the method is invoked
and the variables defined by those declarations are
deallocated when the method completes its execution.
– Stack-dynamic variables are allocated from the run-time stack.
– If scalar, all attributes except address are statically bound.
– Ex:
• Local variables in C subprograms and Java methods.
– Advantages:
• Allows recursion: each active copy of the recursive
subprogram has its own version of the local variables.
• In the absence of recursion it conserves storage b/c all
subprograms share the same memory space for their locals.
– Disadvantages:
• Overhead of allocation and deallocation.
• Subprograms cannot be history sensitive.
• Inefficient references (indirect addressing) is required b/c the
place in the stack where a particular var will reside can only
be determined during execution.
– In Java, C++, and C#, variables defined in methods are by default
stack-dynamic.
int *intnode;
…
intnode = new int; // allocates an int cell
…
delete intnode; // deallocates the cell to which
// intnode points
• Type checking is the activity of ensuring that the operands of an operator are
of compatible types.
• A compatible type is one that is either legal for the operator, or is allowed
under language rules to be implicitly converted, by compiler-generated code,
to a legal type.
• This automatic conversion is called a coercion.
• Ex: an int var and a float var are added in Java, the value of the int var is
coerced to float and a floating-point is performed.
• A type error is the application of an operator to an operand of an
inappropriate type.
• Ex: in C, if an int value was passed to a function that expected a float value,
a type error would occur (compilers didn’t check the types of parameters)
• If all type bindings are static, nearly all type checking can be static.
• If type bindings are dynamic, type checking must be dynamic and done at
run-time.
Strong Typing
• A programming language is strongly typed if type errors are always detected.
It requires that the types of all operands can be determined, either at compile
time or run time.
• Advantage of strong typing: allows the detection of the misuses of variables
that result in type errors.
• Java and C# are strongly typed. Types can be explicitly cast, which would
result in type error. However, there are no implicit ways type errors can go
undetected.
• The coercion rules of a language have an important effect on the value of
type checking.
• Coercion results in a loss of part of the reason of strong typing – error
detection.
• Ex:
int a, b;
float d;
a + d; // the programmer meant a + b, however
– The compiler would not detect this error. Var a would be coerced to float.
Scope
– The scope of a var is the range of statements in which the var is visible.
– A var is visible in a statement if it can be referenced in that statement.
– Local var is local in a program unit or block if it is declared there.
– Non-local var of a program unit or block are those that are visible within the
program unit or block but are not declared there.
Static Scope
– Binding names to non-local vars is called static scoping.
– There are two categories of static scoped languages:
Nested Subprograms.
Subprograms that can’t be nested.
– Ada, and JavaScript allow nested subprogram, but the C-based languages
do not.
– When a compiler for static-scoped language finds a reference to a var, the
attributes of the var are determined by finding the statement in which it was
declared.
– Ex: Suppose a reference is made to a var x in subprogram Sub1. The
correct declaration is found by first searching the declarations of subprogram
Sub1.
– If no declaration is found for the var there, the search continues in the
declarations of the subprogram that declared subprogram Sub1, which is
called its static parent.
– If a declaration of x is not found there, the search continues to the next larger
enclosing unit (the unit that declared Sub1’s parent), and so forth, until a
declaration for x is found or the largest unit’s declarations have been
searched without success. Î an undeclared var error has been detected.
– The static parent of subprogram Sub1, and its static parent, and so forth up to
and including the main program, are called the static ancestors of Sub1.
Ex: Ada procedure:
Procedure Big is
X : Integer;
Procedure Sub1 is
Begin -- of Sub1
…X…
end; -- of Sub1
Procedure Sub2 is
X Integer;
Begin -- of Sub2
…X…
end; -- of Sub2
Begin -- of Big
…
end; -- of Big
– Under static scoping, the reference to the var X in Sub1 is to the X declared in
the procedure Big.
– This is true b/c the search for X begins in the procedure in which the
reference occurs, Sub1, but no declaration for X is found there.
– The search thus continues in the static parent of Sub1, Big, where the
declaration of X is found.
– Ex: Skeletal C#
void sub()
{
int count;
…
while (…)
{
int count;
count ++;
…
}
…
}
– The reference to count in the while loop is to that loop’s local count. The
count of sub is hidden from the code inside the while loop.
– A declaration for a var effectively hides any declaration of a var with the same
name in a larger enclosing scope.
– C++ and Ada allow access to these "hidden" variables
In Ada: Main.X
In C++: class_name::name
Blocks
– Allows a section of code to have its own local vars whose scope is minimized.
– Such vars are stack dynamic, so they have their storage allocated when the
section is entered and deallocated when the section is exited.
– From ALGOL 60:
– Ex:
C and C++:
for (...)
{
int index;
...
}
Ada:
declare LCL : FLOAT;
begin
...
end
Dynamic Scope
The scope of variables in APL, SNOBOL4, and the early versions of LISP is
dynamic.
Based on calling sequences of program units, not their textual layout
(temporal versus spatial) and thus the scope is determined at run time.
References to variables are connected to declarations by searching back
through the chain of subprogram calls that forced execution to this point.
Ex:
Procedure Big is
X : Integer;
Procedure Sub1 is
Begin -- of Sub1
…X…
end; -- of Sub1
Procedure Sub2 is
X Integer;
Begin -- of Sub2
…X…
end; -- of Sub2
Begin -- of Big
…
end; -- of Big
void sub1( )
{
int a, b;
… Í1
} /* end of sub1 */
void sub2( )
{
int b, c;
… Í2
sub1;
} /* end of sub2 */
void main ( )
{
int c, d;
… Í3
sub2( );
} /* end of main */
Variable Initialization
• The binding of a variable to a value at the time it is bound to storage is
called initialization.
• Initialization is often done on the declaration statement.
• Ex, Java
int sum = 0;