CS334 Lecture 6

Contents

  1. BINDING TIME
    1. Variables
    2. Scope
      1. Static
      2. Dynamic scoping:
    3. Lifetime
    4. Value
    5. Confusion over names and locations
  2. The ultimate in bad manners: Pointers
    1. Problems:

BINDING TIME

Program elements have attributes which must be "bound" to them at some point.

Binding = fixing a value or some other property of an object from set of possibilities

MAKING A DECISION

Example: Bind variable to location and value

Time of making a decision is called binding time

Possibilities: Execution, translation, language implementation, language definition

Dynamic

Execution:

  1. Entry to block or subprogram - bind actual to formal parameter, location of local variable.

  2. Arbitrary points - values to variables via assignment

Static

Translation:

  1. Determined by programmer - declarations bind type to variable name, values to constants

  2. Determined by translator - global variable to location (load time) source program to object program representation
Implementation: Representation of values in computer, semantics of operations, statements - if not uniform may lead to diff. results on diff. machines

Language Def: Structure of language, possible types, rep of values in program text.

Example: When is meaning of "+" bound to its meaning in "x + 10"?

Difference between reserved and key words has to do with binding time Example: "DO" is reserved word in Pascal, but not FORTRAN (can write DO = 10)

"Integer" may be redefined in Pascal, but not FORTRAN or Ada.

Why care about binding time?

Early vs. late binding - many language design decisions relate to binding time

Example: "+" bound at translation vs. execution time

Early binding supports compilation, late binding -> interpretation

Small changes may delay binding time -

Ex: recursion forces delay in binding time for local variables to locations

Generally considered useful to bind ASAP

As work down layers in examining or translating language, may find able to make more binding, e.g., by constant propagation - support optimizers.

Bindings are maintained in structures both at compile and at run-time.

During compilation, declarations stored in Symbol table.

Most of these are then used in the compilation process and need not be saved.

Other attributes are needed at execution-time:

Run-time environment keeps track of meanings of names:

Contents of locations also changes during execution. Usually called memory or state:

Memory: Locations -> Values

With interpreter, just keep it all 3 sets of values together in one Environment.

(Notice that your homework interpreter has no run-time environment since there are no identifiers being interpreted yet!)

Variables

What does "N := N + 1" mean?

Variable has 6 components

  1. Name
  2. Type
  3. Location or reference (l-value)
  4. Value (r-value)
  5. Scope - where variable accessible and manipulable - static vs dynamic
  6. Lifetime - interval of time in which location bound to variable

Scope and Lifetime same in some languages - clearly different in some (FORTRAN)

   N := N + 1<
First N refers to location (l-value), second to value (r-value).

Obtaining value of variable called dereferencing. (going from reference or location to value)

Most commonly think of value of variable as changing at run-time, but others can as well.

E.g., name can change (via call by reference parameter)

Aliasing:

Similarly assignment of variables (e.g., x := y) can be by copying or by sharing.

In copy semantics, target variable retains its location, but copies new value from source variable.

In sharing semantics, target variable gets location of source variable, so both share the same location (like objects in Java).

Constants have values, but no location.

One way of classifying a language is according to sorts of entities that can be bound to an identifier. Called denotable values.

In Pascal, objects which can be bound to id's and corresponding declarations are:

Note restrictions in constant def's - irregularity in language

Scope

Scope of a variable is the range of program instructions over which the variable is known.

Static vs Dynamic

Static

Most languages use static scoping (e.g., Pascal, Modula-2, C, ..)

Scope is associated with the static text of program.

Can determine scope by looking at structure of program rather than execution path telling how got there.

May have holes in scope of variable

program ...
	var M : integer;
	....
	procedure A ...
		var M : array [1..10] of real;
		begin
			...
		end;
begin
	...
end.
Variable M declared in main program is not visible in procedure A, since new declaration of M "shades" old declaration.

Symbol table keeps track of which declarations are currently visible.

Think of symbol table as stack. When enter a new scope, new declarations are pushed on and can shade old ones. When exit scope, declarations arising from the scope are popped off.

Dynamic scoping:

Scope is associated with the execution path of program.

In particular an occurrence of an identifier in a procedure may be associated with different variables at different times in the execution of the program.

Example:

program ...
	var A : integer;
	procedure Y(...);
		begin
			...; B := A + B; ...
		end; {Y}
	procedure Z(...);
		var A: integer;
		begin
			...; Y(...); ...
		end; {Z}
	begin {main}
		...; Z(...);...
	end.
Question: Which variable with name A is used when Y is called from Z? With dynamic scoping, symbol table built and maintained at run-time.

Push and pop entries when enter and exit scopes at run-time.

For obvious reasons, dynamic scoping usually implies dynamic typing!

LISP and APL use dynamic scoping (though SCHEME has default of static)

Lifetime

In FORTRAN, variables are allocated statically. All variables are allocated storage before execution of program begins. As consequence, when return to a procedure local variables still have last value left at end of previous invocation. Not so in Pascal.

In Pascal, when enter procedure any local variables are allocated and are then deallocated when exit. (Called dynamic allocation of variables).

In block-structured language (e.g., Pascal, C, Modula-2, etc.):

Use run-time stack to allocate space for local variables and parameters when enter a new unit (procedure, functions, etc.). Space is called an activation record.

Pop it off run-time stack when exit unit.

Note that a procedure may have several activation records on stack if called recursively.

Even without recursion may have several distinct variables on stack with same name!

When pointers are used, utilize another kind of memory, called "heap".

When do "new(p)" operation where p is of type pointer to T, sufficient memory is allocated from the heap to hold a value of type p and p is assigned the location of that memory. The value is accessed by writing p^ (in Pascal).

This memory does not follow the stack discipline. The lifetime of the heap-allocated memory is determined manually by "new" and "dispose" commands. Entering or exiting a scope has no impact on the allocation or deallocation of this memory.

Therefore in Pascal (for example) there are three kinds of memory: static (occupied by global variables), stack-based or automatic (occupied by parameters and local variables of procedures and functions), and heap-based or manually allocated.

In ML, everything comes off of heap. But automatically allocated when needed and deallocated when no way of accessing it (by garbage collector). Java is similar.

More implementation details later.

Value

Usually think of value as bound at execution time, but can vary. If bound at language definition time (e.g., maxint, true, false), then called language defined constant.

If freeze at compilation, then program constant

    const size = 100;
           doubleSize = 2 * size {called manifest constant}
In other languages allow:
	procedure ... (n : integer) is
		var x: constant integer := 3 * n - 2;  
		                     {value bound & frozen on procedure entry}
		     A:  array[1..n] of real;
Some language allow variables to be initialized at declaration.
	var x : integer := 5;
But when is binding done - only first time procedure is entered or every time ? FORTRAN only first time, Java every time.

Postpone discussion of binding time for types until later.

Confusion over names and locations

Two expressions are said to be aliases if they denote the same location:

This can occur especially easily when using var parameters.

Ex: If p(x,y) is a procedure where x and y are both var parameters, then the call p(z,z) makes x and y aliases in the body of p.

If the body of p(x,y) first increases x by one and then y by one, what is the result of the call to p(z,z)? Get z increased by 2! Aliasing often producing surprising (undesirable) behavior in functions.

Also arises when global variable, x, is used inside procedure where x was used as an actual parameter for a var parameter.

Also easy to get aliasing with pointers:

	var x, y: ^ int;
	...
	x := y;
Then x^ and y^ are aliases - any change to one, changes the other!

In languages with assignment by sharing (e.g., Java), get aliasing automatically with all assignments.

The ultimate in bad manners: Pointers

Recognized as major cause of run-time errors.

"Pointers have been lumped with the goto statement as a marvelous way to create impossible to understand programs."

Kernighan & Ritchie, The C Programming Language

(In fairness, they then go on to defend the use of pointers.)

Problems:

  1. If not specify what type they point to (PL/I), then can break type system.

  2. Dangling pointers

    1. If pointers can point to object on run-time stack (named variable - PL/I, C), then object may go away before pointer.

    2. User may explicitly deallocate pointer even if other variables still point to same object. Possible solutions involve reference counting or garbage collection.

  3. Dereferencing uninitialized or nil pointers may cause crashes.

  4. Garbage: Unreachable items may clog heap memory & can't recycle. Garbage collection or reference counting may solve.

  5. Holes in typing system may allow arbitrary integers to be used as pointers (through variant records in Pascal)
Pointer arithmetic norm in C