Variables Six propertie s: 1. name 2. address 3. type 4. value 5. lifetime 6. scope • Binding times of properties: – language specification time – language implementation time – compile time – run time • Broad classification: – static – dynamic
Dec 20, 2015
Variables
Six properties:1. name
2. address
3. type
4. value
5. lifetime
6. scope
• Binding times of properties:– language specification time– language implementation time– compile time– run time
• Broad classification:– static– dynamic
1. Name
• Syntax of language specifies valid names.• Languages used to limit length of names
– BASIC (1978): [A-Z]([0-9])– Fortran77: 6 characters– Fortran95: 31 characters– Java, C#: no limit
• Restrictions:– keywords have special meaning (e.g. ‘for’)– reserved words cannot be used as names– generally keywords are reserved
2. Address
• The memory address at which the value associated with the name is stored.
• This is called the l-value (because it is the value used on the left hand side of an assignment operator). In Scheme terms, an unevaluated symbol.
• A given name can be associated with multiple addresses during program execution (e.g. parameters of a recursive function, an instance variable defined in a multiply-instantiated class).
• Aliasing occurs when many variables share an address.
3. Type
• The type of a variable determines the set of values which can be associated with the name.
• We will return to type binding and type checking.
Aside: binding times of types
• The binding time of a set of values to a type differs across languages.
• In Java the size and representation for types is written into the language specification.– every Java implementation is guaranteed to have the
same range of values for the primitive types.• In C the binding of a set of value to types is left
to implementation time.– different implementations can (and do) make different
decisions– implication is that the very same program can behave
differently on different platforms.
Binding types to names (1)
• Explicit vs. implicit– an explicit declaration specifies the type of a variable– an implicit declaration is a language convention giving
name-type mappings• Fortran: “If the identifier begins with one of the letters I, J, K,
L, M or N, or their lowercase versions, it is implicitly declared to be Integer type, otherwise it is implicitly declared to be Real type.” [p. 213]
– Perl: different namespaces for different types• $ implies scalar• @ implies array• % implies hash
Binding types to names (2)
• Static vs. dynamic– with static binding the type of a variable is
known at compile time (e.g. C, C++, Java)– with dynamic binding the type of a variable is
not known until run time (e.g. Scheme, JavaScript), and can change during the execution of a program [pg. 214]:list = [10.2, 3.5];
list = 47;
Binding types to names (3)
• Type inferencing (ML)• ML derives the types of all expressions
from the types of their constituent parts.• ML (with very few exceptions) does not
require any type declarations, and yet all expressions are fully typed at compile time.
• ML uses type variables to express generic types (e.g. ’a and ”a).
4. Value
• The r-value of the variable (because it is the value used on the right hand side of an assignment operator). In Scheme terms, the value retrieved from symbol lookup in an environment.
• This is the contents of a block of memory cells, whose starting address is the l-value of the variable, whose size and interpretation are determined by the type of the variable.
• Sometimes we refer to this block of memory, typically many bytes large, simply as the “memory cell” of a variable.
Aside: memory footprint & representation
• The type of a variable determines two very important things:– the amount of memory allocated to the variable– the representation scheme used to write/read bit patterns
into/from memory.
• Consider the type short in C. Using the default ‘cc’ compiler on pollux, we find that a short occupies 8 bits; integers are stored using the 2’s complement representation scheme.
Anatomy of a short
The range of values we can store in a short is -128 to +127:
BIT PATTERN DECIMAL VALUE01111111 +12701111110 +126 . . . . . . . .00000010 +200000001 +100000000 011111111 -111111110 -2 . . . . . . . .10000001 -12710000000 -128
Anatomy of an int
The range of values we can store in an int is -32768 to +32767:
BIT PATTERN DECIMAL VALUE0111111111111111 +327670111111111111110 +32766 . . . . . . . .0000000000000010 +20000000000000001 +10000000000000000 01111111111111111 -11111111111111110 -2 . . . . . . . .1000000000000001 -327671000000000000000 -32768
What happens here?
int i = 128;
short s = i;
5. Lifetime
• “The lifetime of a variable is the time during which the variable is bound to a specific memory location.” [p. 219]
• “…the lifetime of a variable begins when it is bound to a specific cell and ends when it is unbound from that cell.” [p. 219]
• Four categories– static– stack-dynamic– explicit heap-dynamic– implicit heap-dynamic
Memory organization
AVAILABLEMEMORY
STATICcode &
static data
HEAPdynamic data
STACKlocal data:
invocation records
static variables
• Bound to a memory location prior to execution.
• No run-time allocation needs to occur: efficient.
• Persistent: variable persists throughout execution of a program.
Example (C)#include “stdio.h”;int counter() { static int k = 0; return ++k;}int main() { printf(“Counter is %d \n”,counter()); printf(“Counter is %d \n”,counter()); return 0;}
/* OUTPUT IS: Counter is 1 Counter is 2*/
Notes about example
• static variable k is allocated space in the static segment
• allocation happens once
• k’s lifetime is the that of the entire program
• k’s value persists from call to call
stack dynamic variables
• “storage bindings are created when their declaration statements are elaborated, but whose types are statically bound” [p. 220]
• Allocated on the run-time stack
Example (C)#include “stdio.h”;int counter() { int k = 0; return ++k;}int main() { printf(“Counter is %d \n”,counter()); printf(“Counter is %d \n”,counter()); return 0;}
/* OUTPUT IS: Counter is 1 Counter is 1*/
Notes about example
• stack-dynamic variable k is allocated space in the stack segment
• allocation happens each time function is called
• k’s lifetime is the that of the function invocation
• k’s value does not persist from call to call: it is reinitialized on each function execution
explicit heap-dynamic variables
• anonymous (nameless) variables created at runtime, allocated on the heap, and accessible only via indirection (a pointer)
• Example [p. 221]
int *intnode; // Create a pointer...intnode = new int; // Create the heap-dynamic variable...delete intnode; // Deallocate the heap-dynamic variable // to which intnode points
Example (Java)public class Count private int k; public Count() { k = 0; } public int counter() { return ++k; } public static void main(String [] args) { Count c = new Count(); System.out.println(“Counter is ”+c.counter()); System.out.println(“Counter is ”+c.counter()); }}/* OUTPUT IS: Counter is 1 Counter is 2*/
Notes about example
• instance variable k is allocated space in the heap segment
• allocation happens each time object is created
• k’s lifetime is the that of its object
• k’s value persists from call to call of the method
• many different independent k’s can co-exist
implicit heap-dynamic variables
• automatic heap-allocation• JavaScript, [pg. 214]:
list = [10.2, 3.5];list = 47;
• Scheme– all allocation is done on heap– cons allocates a pair– environments are allocated on heap (so no
runtime stack is needed for function calls).
Example (Scheme)(define count1 (lambda () (let ((k 0)) (set! k (+ k 1)) k)))(define count2 (let ((k 0)) (lambda () (set! k (+ k 1)) k))) (display (count1)) (newline)(display (count1)) (newline)(display (count2)) (newline)(display (count2)) (newline)/* OUTPUT IS:1112*/
Environment diagrams
(drawn on board)
Notes about example
• in count1 k is initialized each time function is called
• in count2 k is initialized when the function is defined
Exercise
• How do you define a Scheme function which can dynamically generate counter functions, each with its own independent counter?
counterMaker
(define counterMaker (lambda () (let ((k 0)) (lambda () (set! k (+ k 1)) k))))(define c1 (counterMaker))(define c2 (counterMaker))(c1) (c1) (c1) (c2) (c2) (c1) (c2)
6. Scope
• “The scope of a variable is the range of statements in which the variable is visible. A variable is visible in a statement if it can be referenced in that statement.”
• Two basic approaches:– static scoping– dynamic scoping
Static scope
Static scoping (also called lexical scoping): the scope of a variable is determined by the static (lexical) structure of the program.
– scope determined by lexical nesting– some languages allow nested blocks
• we’ve seen examples in C and Java
– some languages allow nested functions• C/C++/Java do not allow this• Scheme/ML do
Dynamic scope
• dynamic scope– scope determined by (dynamic) call history– we will see an example soon
Why do we care?
• Obviously important for our understanding of programs.
• More importantly, we use the scoping of variables to support encapsulation.
• Strong encapsulation lets us build robust components:– grants access to those components which need it– denies access to all other components
Collection classesEach collection defines its own Iterator.
Each collection needs to keep its implementation details private.
Each iterator is defined as a separate class (so it can be instantiated independently of the class).
Each iterator encapsulates iteration logic for a specific collection. It must know the implementation details in order to serve as the bridge between clients of the collection and the internal workings of that collection.
How do we grant access to Iterator classes while denying access to others?
Inner classes
• Java’s Collection classes– concrete collection class must hide its
implementation– must provide Iterator– Iterator must know implementation details– Inner class structure embeds Iterator
definition within scope of collection class– Iterator can access internals of collection
without collection having to break encapsulation
Nested functions• Create nested scopes to hide bindings• Allows us to hide helper functions (similar to what
inner classes let us do):
(define fact
(letrec ((helper
(lambda (n acc)
(if (= n 0)
acc
(helper (- n 1) (* n acc))))))
(lambda (n)
(helper n 1))))
Examples comparingstatic and dynamic
scope
• Example in C-like language
• Example in Scheme-like language
Dynamic vs. static scope:C-like language
int a = 3;
void foo(){ printf(“a has value %d \n”,a); }
void bar(){ int a = 5; foo();}
foo(); // what does this print?
bar(); // what does this print?
With static scope
int a = 3;
void foo(){ printf(“a has value %d \n”,a); }
void bar(){ int a = 5; foo();}
void abc(){ int a = 7; foo();}
foo(); // what does this print?
bar(); // what does this print?
abd(); // what does this print?
C is statically scoped, so the output will be:a has value 3
a has value 3
a has value 3
With dynamic scope
int a = 3;
void foo(){ printf(“a has value %d \n”,a); }
void bar(){ int a = 5; foo();}
void abc(){ int a = 7; foo();}
foo(); // what does this print?
bar(); // what does this print?
abd(); // what does this print?
If C were dynamically scoped, the output would be:a has value 3
a has value 5
a has value 7
Languages with dynamic scope
• Common Lisp and Perl give the programmer the option of using either dynamic or static scope.
Dynamic vs. static scope:Scheme-like language
(define a 3)
(define f (lambda () a))
(define g (lambda (a) (f)))
(f)
(g 7)
Environment:
a 3
(define a 3)
Environment:
a
f
3
()
a
(define a 3)
(define f (lambda () a))
Environment:
a
f
g
3
()
a
(a)
(f)
(define a 3)
(define f (lambda () a))
(define g (lambda (a) (f)))
a
f
g
3
()
a
(a)
(f)
a
Environment during evaluation of (f):
static linkdynamic link
(f)
=> 3=> 3
(define a 3)
(define f (lambda () a))
(define g (lambda (a) (f)))
(f)
a
f
g
a 7
3
()
a
(a)
(f)
a
(f)
static linkdynamic link
Environment during evaluation of (g 7):
=> 7=> 3
(g)
(define a 3)
(define f (lambda () a))
(define g (lambda (a) (f)))
(f)
(g 7)
Questions?