VARIABLES ADRESSES AND VALUES

17.01

The memory of the computer consists of a list of memory locations.
Each memory location is characterized by an address, say 00dbe17e. 
The content of a memory location is called its value, say 01010001.
Memory locations are used to store and retrieve values.

In a high-level language memory locations are not known as adresses but they
are known by name: the name of a constant, a variable, an array or a function.

Let us concentrate on variables:
The notion of a variable is an abstraction of a memory location or address.

When a variable is declared, say,
	int n;
the compiler binds the name n to a free address in memory. 
The values that the variable can hold are stored at this address
	n = 81; 
and this value can be accessed, retrieved and changed using its name
	n = 2*n; 

API



17.01

TABLE OF CONTENTS HC 17

17.02

 

LHS VALUE AND RHS VALUE

17.02

Each variable has a type in order to be able to interpret the value of what
is stored at its address. Thus

	•  if n is of type int and its address contains bitpattern 0..001010001
	   then its value will be 81,
	•  if c is of type char and its address holds 01010001
	   then its value will be 'Q'.

Now what are variables?
Variables are essentially entities with two attributes:
 
•	address or lhsvalue              where does the entity exist?
•	content or rhsvalue or value     what does the entity contain?

Assignment is the abstraction of updating the contents of a memory location.

Let me make this clear using the update assignment n = 2*n; 
Here the name of the variable n occurs in two different roles:
•	on the righthand side (rhs) the name n stands for its value
	and the expression 2*n evaluates to 162
•	on the lefthand side (lhs) the name n stands for its address
	and n = 162; means that 162 will be stored at this address.

API



17.01

TABLE OF CONTENTS HC 17

17.03

 

DECLARATIONS ASSIGNMENTS AND COMPILER ACTIONS

17.03

The essential difference between address (lhsvalue) and value (rhsvalue) can 
be made more clear even by putting the following question:
Did you ever write 13 = n;?
I do not think so: 
	rhsvalues cannot be written on the lhs of an assignment!
	
DECLARATION, ASSIGNMENT, COMPILER ACTIONS and MEMORY MAP:

Declaration:         int  anInt;
Assignment:         anInt = 13;
Compiler actions:
•	allocate i bytes of memory (00dbe17e .. 00dbe181)
	with i the number of bytes required to store an integer
•	add name, type, address, ..  to internal symboltable
•	store value 13 at address of anInt.

	

API



17.02

TABLE OF CONTENTS HC 17

17.04

 

DECLARATIONS ASSIGNMENTS AND MEMORY MAPS

17.04

This memory map (and many that follow) shows:

•	the name of the variable

•	the address chosen by the compiler  
		where does the variable exist?    i.e. its lhsvalue or address

•	the value that has been stored
		what does the variable contain?   i.e. its rhsvalue or simply value.

Since real addresses are awkward to write and read (as demonstrated in the 
previous picture) we will simplify them to 1004-1007, 1008-1011, ....

If a variable has not been initialized we will write ? to denote its value.

	

API



17.03

TABLE OF CONTENTS HC 17

17.05

 

THE POINTER CONCEPT

17.05

Let us take a bold step!
•	a lhsvalue is an address
•	declare variables that can hold an address: why not?

A variable that can hold an address is called a pointer variable 
or simply a pointer or reference.

A pointer is said to point at or refer to the address it holds 
for reasons that will soon be clear, I hope.

In C a pointer variable is declared by preceding its name by *:

	int *anIntPtr;
	
declares a pointer variable with the name anIntPtr. 

Reading right to left:
•	anIntPtr is a pointer to an integer, or
•	*anIntPtr is an expression that evaluates to an integer.

API



17.04

TABLE OF CONTENTS HC 17

17.06

 

DECLARATION OF POINTER

17.06

Declaration: int *anIntPtr;

Compiler actions:
•	allocate a bytes of memory (00abc872 .. 00abc875)
	with a the number of bytes required to store an address
•	add name, type, address to internal symboltable

		

The variable anIntPtr has no value yet: the pointer is not initialized.

Our intention is to initialize this pointer 
by assigning an address where you can find an integer.

API



17.05

TABLE OF CONTENTS HC 17

17.07

 

ASSIGNMENT OF POINTER

17.07

So let us assign the value 00dbe17e of our previous variable anInt:

	int  anInt;
	int *anIntPtr;
	     anIntPtr = &anInt;

		


Indeed:
	&anInt is the C expression for address of the variable anInt. 

API



17.06

TABLE OF CONTENTS HC 17

17.08

 

THE ADDRESS OPERATOR &

17.08

The assignment of the address of the int variable anInt to the
to the pointer variable anIntPtr is written in C as

	anIntPtr = &anInt;

Now the memory map has the following appearance:

		

The address operator & determines the address of the variable anInt
and this address is assigned to the pointer variable anIntPtr.

We say:
•	anIntPtr points at or refers to anIntanIntPtr holds the address of anInt.

API



17.07

TABLE OF CONTENTS HC 17

17.09

 

THE DEREFERENCING OPERATOR *

17.09

An important question now is how to get the value of the variable anInt
using anIntPtr which is the pointer that points at it!

After declaration and initialization:
	int n1, n2, *nPtr;
	int n1 = 13;
	nPtr = &n1;

		

The value of n1 can be retrieved (and stored into n2) in an indirect way

	n2 = *nPtr;
	
and now n2 holds the value 13 as well!

API



17.08

TABLE OF CONTENTS HC 17

17.10

 

SUMMARY

17.10

Indeed after n2 = *nPtr; the variable n2 is equal to 13.
	
		

The operator * is called the dereferencing operator.
The evaluation of expression *nPtr is called dereferencing the pointer.
The idea behind this process should be clear:
•	nPtr holds the address of n1 (which is &n1 here 1004)
•	*nPtr is an expression of type int
•	evaluation yields the value stored at &n1 that is *&n1 or simply n1.

Summary int n, *pn; pn = &n;n    is a variable of type intpn   is a variable of type pointer to int&n   is an expression that evaluates to the address of n*pn  is an expression that evaluates to the value of n.

API



17.09

TABLE OF CONTENTS HC 17

17.11

 

PROGRAM 1

17.11

A small program to display a memory map:

#include <stdio.h> // needed for printf()

int main(void) {

	int j = 13,
	    k = 47, 
	   *p = &k; // declare pointer p and initialize it

	printf("j is %d and its address is %p\n", j, &j);
	printf("k is %d and its address is %p\n", k, &k);
	printf("p is %p and its address is %p\n", p, &p);

	return(0);
}

API



17.10

TABLE OF CONTENTS HC 17

17.12

 

SAMPLE RUN AND MEMORY MAP

17.12


SAMPLE RUN:

	j is 13 and its address is 00bc2610          i.e. &j
	k is 47 and its address is 00bc260c          i.e. &k
	p is 00bc260c and its address is 00bc2608    i.e. &p



		


API



17.11

TABLE OF CONTENTS HC 17

17.13

 

PROGRAM 2

17.13

A small program that summarizes all features discussed sofar.

#include <stdio.h>
int main(void) {

	int  j = 13,
	     k = 47, 
	    *p = &k;

	printf("%p : address of pointer variable p\n",           &p);
	printf("%p : the value of p is the address of k\n",       p);
	printf("%p : the address k (checked)\n",                 &k);
	printf("%d : the value *p is the value of k\n",          *p);
	printf("%d : the value of k (checked)\n",                 k);
	*p = 87;  // change whatever p points at into 87
	printf("%d : the new value *p\n",                        *p);
	printf("%d : the value of k (indeed k has changed)\n",    k);
	printf("%d : the value of k again (i like playing)\n", **&p);
	return(0);
}

API



17.12

TABLE OF CONTENTS HC 17

17.14

 

SAMPLE RUN AND MEMORY MAP

17.14

MEMORY MAP:

		


SAMPLE RUN:
	
	00bc2388 : address of pointer variable p             &p
	00bc238c : the value of p is the address of k        p == &k
	00bc238c : the address k (checked)                   &k
	47 : the value *p is the value of k
	47 : the value of k (checked)
	87 : the new value *p
	87 : the value of k (indeed k has changed)
	87 : the value of k again (i like playing)

API



17.13

TABLE OF CONTENTS HC 17

17.15

 

POINTER SYNTAX AND PITFALLS

17.15

Declarations

•	int*p;
•	int  *p;
•	int*  p;
•	int * p;

are essentially identical from the compiler's point of view.

But
	int*a, b;       declares variable a of type pointer to int
	                        and variable b of type int.

In order to declare two pointers to int you have to write  

	int *a, *b;
 

API



17.14

TABLE OF CONTENTS HC 17

17.16

 

INITIALIZATIONS

17.16

Initializations

	int k, *p;      // declare k as int and p as pointer to int
	p = &k;         // initialize p
	*p = 87;     // initialize k

can also be written as
	
	int k,          // declare k as int 
	   *p = &k;     // init pointer p to int at address of k
	*p = 87;     // initialize k

Do not confuse the expressions

	int *p = &k;
 
which declares pointer p and assigns the address value &k
and
	*p = 87;
     
which assigns value 87 to k (which is pointed at by p).

API



17.15

TABLE OF CONTENTS HC 17

17.17

 

CONFUSION AND WARNING

17.17

This confusion can be avoided easily using the typedef mechanism of C:

	typedef int* pint;   
	// pint is a new type name for pointer to int
	int k = 87;    // declare int k and initialize on 87
	pint p = &k; // declare pint p and initialize on &k
	*p = k + 3;    // now *p is equal to 90 and so is k!

WARNING:
Consider the following fragment

	int k = 47, *p;       declare and initialize int k
	                      and declare pointer p to int
	*p = 87;              ERROR!!!
	
	Indeed:
	•	pointer p has been declared but not initialized!!p points nowhere! (but ...)
	•	and the assignment is an error that will not be detected!

ALWAYS INITIALIZE YOUR POINTERS!!

API



17.16

TABLE OF CONTENTS HC 17

17.18

 

ALWAYS INITIALIZE POINTERS

17.18


ALWAYS INITIALIZE YOUR POINTERS!!

•	One way is by p = NULL; 
	And now the error will be detected since 
	a null-pointer cannot be dereferenced.

•	The other way has been shown above p = &k;
	a valid address is assigned to pointer p
	and now pointer p refers to integer k 
	and k can be changed using *p.

Notes:

•	NULL is the universal pointer constant that can be assigned to any pointer.printf("%p", NULL); yields 00000000.

API



17.17

TABLE OF CONTENTS HC 17

17.19

 

CALL BY VALUE AND CALL BY REFERENCE

17.19

Functions in C

	returnType functionName(formalParameterList) {
		// definition or implementation
	}
	
	returnType
	•	void
	•	any value type
	•	any pointer type
	
	formalParameterList
	•	void
	•	comma-separated sequence of formalParameter
	
	formalParameter
		typeIdentifier followed by variableIdentifier

NB:
	function prototype:
		returnType functionName(formalParameterList);

Function prototypes are required in ANSI C!

API



17.18

TABLE OF CONTENTS HC 17

17.20

 

IMPORT AND EXPORT PARAMETERS

17.20

The formal parameters can always be classified:

•	VALUE PARAMETERS
	are used to import the necessary data for the function to do its job.
	
The import mechanism is known as CALL BY VALUE
and its properties will be summarized below.

•	REFERENCE PARAMETERS
	are used to export the results after the function has done its job,
	can also be used for import: the function modifies the imported data
	and exports the modified data using the same parameters.

The export mechanism is known as CALL BY REFERENCE
and this mechanism (in the language C) will be discussed thoroughly.

First consider some examples to get the idea of import and export.

	int min(int n1, int n2) {
		// determine minimum of n1 and n2 and return result
	}
	usage:	theMin = min(a, b); // function min imports a and b 

API



17.19

TABLE OF CONTENTS HC 17

17.21

 

IMPORT AND EXPORT EXAMPLES

17.21

Alternatively:

	void min(int n1, int n2, int *min) {
	//  determine min of n1 and n2 and return result by ref
	}
	usage:	min(a, b, &theMin);
Import a, b and &theMin. 
Export minimum of a and b, i.e. theMin.

	void minmax(int n1, int n2, int *min, int *max) {
	//  determine min and max of n1 and n2 and return by ref
	}
	usage:	min(a, b, &theMin, &theMax);
Import a, b, &theMin and &theMax.
Export minimum and maximum of a and b, i.e. theMin and theMax.

	void sort3(int *n1, int *n2, int *n3) {
	// sort n1, n2 and n3 and return results by ref
	}
	usage:	sort3(&a, &b, &c);
Import &a, &b and &c.
Export a, b and c such that a <= b <= c. 

API



17.20

TABLE OF CONTENTS HC 17

17.22

 

CALL BY VALUE: PROTOTYPE EXAMPLE

17.22

#include<stdio.h> // we use printf()
void test(int);      // prototype
	
int main(void) {
	int k = 5;
	printf("before calling test() k is %d.\n", k);  // 5
	test(k);
	printf("after  calling test() k is %d.\n", k);  // 5
}
	
void test(int n) {
	printf("inside test() n is %d.\n", n);    // 5
	printf("now we change n.\n");
	n++;
	printf("now we have changed n.\n");
	printf("inside test() n is %d.\n", n);    // 6
}

API



17.21

TABLE OF CONTENTS HC 17

17.23

 

CALL BY VALUE: ESSENTIALS

17.23

What happens exactly?

At the moment that the function test() is called:

•	memory is created for formal parameter nvalue 5 of actual argument k is copied into formal parameter n.

During execution of the function test():

•	the value of the local variable n is used and even changed
•	but whatever happens with n will have no effect on k at all!!

After termination of the function test():

•	local variables that correspond with the formal parameters 
	do not exist any longer
•	the value of the variable k that lives outside the function
	is still 5.

API



17.22

TABLE OF CONTENTS HC 17

17.24

 

CALL BY VALUE: REMARKS

17.24

1 	
	prototype  void f(int n);    formal parameter n of type int
	                             return type void
	usage      f(e);             expression e that yields an int
	
Expression e will be evaluated and yields an integer value 
formal parameter n imports this value. 
	
This type of function is not very useful since it does not return anything
but writing output to the screen is a useful example nevertheless!

2.1
	prototype  int  f(int n);    formal parameter n of type int
	                             return type int
	usage      int res = f(e);   expression e that yields an int

Expression e will be evaluated and yields an integer value
formal parameter n imports this value, 
the function uses this value to calculate a result, 
the result is returned and assigned to res.

API



17.23

TABLE OF CONTENTS HC 17

17.25

 

CALL BY VALUE: REMARKS

17.25

2.2
	prototype  int  f(int n);    formal parameter n of type int
	                             return type int
	usage      int res = f(k);   actual variable k of type int

This is a special case of the previous one 
since a variable is a special case of an expression.
Formal parameter n imports a copy of the value of the actual variable k,
the function uses this value to calculate a result, 
the result is returned and assigned to res.

2.3
	usage      int n = f(n);   actual variable n of type int

This is a special case of the previous one 
where actaul variable and formal parameter are both called n.
Formal parameter n imports a copy of the value of the actual variable n,
the function uses this value to calculate a result 
the result is returned and assigned to the actual variable n.
This one is of interest since now the result of the calculation
is assigned to the original actual argument and this is the only way
in which the actual variable can have its value changed!

API



17.24

TABLE OF CONTENTS HC 17

17.26

 

CALL BY REFERENCE: DESIGN

17.26

For the moment we will use the following conventions:
-	variables are of type int and of type pointer to int,
-	formal parameters are represented by lower case letters, and
-	actual variables are represented by corresponding upper case letters.

If function f has a formal parameter of type pointer to int, i.e. int*
then its invocation requires the address of an int variable &N 
	declaration    void f(int* n);
	usage          f(&N);

What happens at calling time?

•	address &N is copied into formal parameter n
	and now we have two memory locations n and N such that
	•	n contains the address of N
	•	and N contains an integer value,
	•	*n is an expression that evaluates to the value of N
		and we say: N and *n are synonomous, 
•	what happens with *n during execution of the function f
	also happens with the actual variable N since n equals &N. 

API



17.25

TABLE OF CONTENTS HC 17

17.27

 

CALL BY REFERENCE: EXAMPLE

17.27


#include<stdio.h>     // we use printf()

void test(int p, int* q); // prototype

	
int main(void) {
	int k = 5;
	printf("before calling test() k is %d.\n", k);   // 5
	test(k, &k);
	printf("after  calling test() k is %d.\n", k);   // 6
}
void test(int p, int*q) {
	printf("inside test(): p = %d and *q = %d.\n", p, *q);   
	                                            // 5   5
	printf("now we increase the value of *q.\n");  (*q)++;
	printf("inside test(): p = %d and *q = %d.\n", p, *q);   
	                                            // 6   6
}

API



17.26

TABLE OF CONTENTS HC 17

17.28

 

CALL BY REFERENCE: int getInput(void)

17.28

1	Get input directly from scanf()
int main(void) {
	int N;
	printf("an int please.\n>>\t"); // get input
	scanf("%d", &N);                // store at address of N
	printf("you entered %d.\n", N); // print to stdout
}

2	Now using a function int getInput(void);

int getInput(void) {
	int k;                          // local variable k
	printf("an int please.\n>>\t"); // user enters 123
	scanf("%d", &k);                // store 123 at &k
	return(k);                      // and return value of k
}
int main(void) {
	int N;
	N = getInput();                 // ask user for value
	                                // assign value to N
	printf("you entered %d.\n", N); // print value of N
}

API



17.27

TABLE OF CONTENTS HC 17

17.29

 

CALL BY REFERENCE: void getInput(int*)

17.29


3	Now using a call-by-ref function void getInput(int* n);

void getInput(int* n) {             // n is address of int
	printf("an int please.\n>>\t"); // user enters 123
	scanf("%d", n);                 // store value at n
}

NOTA BENE!! scanf("%d", n); AND NOT scanf("%d", &n);
 
int main(void) {
	int N;
	getInput(&N);                   // ask user for value
	                                // assign value to N
	printf("you entered %d.\n", N); // print value of N
}

API



17.28

TABLE OF CONTENTS HC 17

17.30

 

SWAPPING: void swap(int*, int*)

17.30

void swap(int* jPtr, int* kPtr) {
	int temp = *jPtr; *jPtr = *kPtr; *kPtr = temp;
}
int main(void) {
	int n1 = 13, n2 = 47;
	swap(&n1, &n2);
	return(0);
}
	
	

API



17.29

TABLE OF CONTENTS HC 17

17.31

 

SWAPPING: void swap(int*, int*)

17.31


void swap(int* jPtr, int* kPtr) {
	int temp = *jPtr; *jPtr = *kPtr; *kPtr = temp;
}
	
	
	

API



17.30

TABLE OF CONTENTS HC 17

17.32

 

SWAPPING: void swap(int*, int*)

17.32

At last
	

and indeed 
	int main(void) {
		int n1 = 13, n2 = 47;
		printf("n1 = %d and n2 = %d \n", n1, n2);
		swap(&n1, &n2);
		printf("n1 = %d and n2 = %d \n", n1, n2);
		return(0);
	}

prints 
	n1 = 13 and n2 = 47
	n1 = 47 and n2 = 13

API



17.31

TABLE OF CONTENTS HC 17

17.33

 

RELATED EXAMPLES: void sort2(int*, int*)

17.33

Sort two integer values:

	void sort2(int* min, int* max) {
		if (*min > *max) 
			swap(min, max);
	}


Determine minimum and maximum of three integers:
Design a function with 
•	three import parameters n1, n2 and n3
•	two export parameters *min and *max


void minmax(int n1, int n2, int n3, int* min, int* max) {
	int min23 = ((n2 < n3) ? n2 : n3),
		max23 = ((n2 > n3) ? n2 : n3);
	*min = ((n1 < min23) ? n1 : min23);
	*max = ((n1 > max23) ? n1 : max23);
}

API



17.32

TABLE OF CONTENTS HC 17

17.34

 

RELATED EXAMPLES

17.34

Let main() be defined by:

int main(void) {
	int p = 7, q = 13, r = 2, 
	    MIN, MAX;
	minmax(p, q, r, &MIN, &MAX);
	printf("mini of %d and %d and %d is %d\n", p, q, r, MIN);
	printf("maxi of %d and %d and %d is %d\n", p, q, r, MAX);
}

Now what happens here?
•	the values 7 and 13 and 2 are copied into n1 and n2 and n3
•	the addresses &MIN and &MAX are copied into min and max
•	the smallest value is determined by *min = ...; 
	and stored at address min
	this address is also known as &MIN since min is a copy of &MIN
	consequently the value of MIN i.e. *min is now 2
•	the largest value is determined by *max = ...; 
	and stored at address max
	this address is also known as &MAX since max is a copy of &MAX
	consequently the value of MAX is now 13

API



17.33

TABLE OF CONTENTS HC 17

17.35

 

RELATED EXAMPLES: void sort3(int*, int*, int*)

17.35


NOTA BENE:before calling the function minmax 
	the values of MIN and MAX are undefined!
•	after termination they contain the smalles and largest of p, q and r
•	in this case min and max are often called pure export parameters.
but
formal reference parameters can also be used to 
import data that will be modified and then exported 
using the same parameters, as in the next example. 

	void sort3(int* min, int* mid, int* max) {
		sort2(min, mid);	// now *min <= *mid
		sort2(min, max);	// now *min <= *max
		sort2(mid, max);	// now *mid <= *max
		               	// hence *min <= *mid <= *max
	}

API



17.34

TABLE OF CONTENTS HC 17

17.36

 

LAST EXAMPLE: void remquo(int*, int*)

17.36

For any two given values n1 and n2 
determine remainder and quotient by reference.
=>
Design a function 
•	with two reference parameters *n1 and *n2 
	that take care of both import and export
•	addresses of integer values a and b are imported
•	integer values a%b and a/b are exported using *n1 and *n2, resp.

	void remquo(int* n1, int* n2) {
		int rem = *n1 % *n2,
		    quo = *n1 / *n2;           // take care!!
		*n1 = rem;
		*n2 = quo;
	}

usage:
	int A = 105, 
	    B =  17;
	remquo(&A, &B); // and now A == 3 and B == 6

Note that the original values 105 and 17 are lost forever!
They have been overwritten by the values 3 and 6, respectively.

API



17.35

TABLE OF CONTENTS HC 17

17.01

 

TABLE OF CONTENT HC 17

TOC

01: Variables, addresses and values
02: Lhs value and rhs value
03: Declarations and compiler actions
04: Declarations and memory maps
05: The pointer concept
06: Declaration of pointer
07: Assignment of pointer
08: The address operator &
09: The dereferencing operator *
10: Summary
11: Program 1
12: Sample run and memory map
13: Program 2
14: Sample run and memory map
15: Pointer syntax and pitfalls
16: Initializations
17: Confusing and warning
18: Always initialize pointers
19: Call by value and call by reference
20: Import and export parameters
21: Import and export examples
22: Call by value: prototype example
23: Call by value: essentials
24: Call by value: remarks
25: Call by value: remarks
26: Call by reference: design
27: Call by reference: example
28: Call by reference: int getInput(void)
29: Call by reference: void getInput(int*)
30: Swapping: void swap(int*, int*)
31: Swapping: void swap(int*, int*)
32: Swapping: void swap(int*, int*)
33: Related examples
34: Related examples
35: Related examples
36: Last example

API



TOC