10 Pointers
10 Pointers
Pointers
15-122: Principles of Imperative Computation
Frank Pfenning, Rob Simmons
Lecture 10
September 28, 2013
1 Introduction
In this lecture we complete our discussion of types in C0 by discussing
pointers and structs, two great tastes that go great together. We will dis-
cuss using contracts to ensure that pointer accesses are safe, as well as the
use of linked lists to implement the stack and queue interfaces that were
introduced last time. The linked list implementation of stacks and queues
allows us to handle lists of any length.
Relating this to our learning goals, we have
Algorithms and Data Structures: Linked lists are a fundamental data struc-
ture.
Programming: We will see structs and pointers, and the use of recursion in
the definition of structs.
locally, and pass to functions are just the values themselves; the picture we
work with looks like this:
char c ‘\n’ 0 1 2 3 4
string[] A
struct img_header {
pixel[] data;
int width;
int height;
};
Here data, width, and height are not variables, but fields of the struct.
The declaration expresses that every image has an array of data as well as a
width and a height. This description is incomplete, as there are some miss-
ing consistency checks – we would expect the length of data to be equal to
the width times the height, for instance, but we can capture such properties
in a separate data structure invariant.
Structs do not necessarily fit into a machine word because they can
have arbitrarily many components, so they must be allocated on the heap
(in memory, just like arrays). This is true even if they happen to be small
enough to fit into a word (in order to maintain a uniform and simple lan-
guage implementation).
% coin structdemo.c0
C0 interpreter (coin) 0.3.2 ’Nickel’
Type ‘#help’ for help or ‘#quit’ to exit.
--> struct img_header IMG;
<stdio>:1.1-1.22:error:type struct img_header not small
[Hint: cannot pass or store structs in variables directly; use
pointers]
We can access the fields of a struct, for reading or writing, through the
notation p->f where p is a pointer to a struct, and f is the name of a field
in that struct. Continuing above, let’s see what the default values are in the
allocated memory.
--> IMG->data;
(default empty int[] with 0 elements)
--> IMG->width;
0 (int)
--> IMG->height;
0 (int)
We can write to the fields of a struct by using the arrow notation on the
left-hand side of an assignment.
char c ‘\n’ 0 1 2 3 4
0
1
2
0xFF00FF00
0xFFFF0000
3 Pointers
As we have seen in the previous section, a pointer is needed to refer to a
struct that has been allocated on the heap. In can also be used more gener-
ally to refer to an element of arbitrary type that has been allocated on the
heap. For example:
In this case we refer to the value using the notation *p, either to read (when
we use it inside an expression) or to write (if we use it on the left-hand side
of an assignment).
So we would be tempted to say that a pointer value is simply an ad-
dress. But this story, which was correct for arrays, is not quite correct for
pointers. There is also a special value NULL. Its main feature is that NULL is
not a valid address, so we cannot dereference it to obtain stored data. For
example:
char c ‘\n’ 0 1 2 3 4
4 Linked Lists
Linked lists are a common alternative to arrays in the implementation of
data structures. Each item in a linked list contains a data element of some
type and a pointer to the next item in the list. It is easy to insert and delete
elements in a linked list, which are not natural operations on arrays, since
arrays have a fixed size. On the other hand access to an element in the
middle of the list is usually O(n), where n is the length of the list.
An item in a linked list consists of a struct containing the data element
and a pointer to another linked list. In C0 we have to commit to the type
of element that is stored in the linked list. We will refer to this data as
having type elem, with the expectation that there will be a type definition
elsewhere telling C0 what elem is supposed to be. Keeping this in mind
ensures that none of the code actually depends on what type is chosen.
These considerations give rise to the following definition:
struct list_node {
elem data;
struct list_node* next;
};
typedef struct list_node list;
struct infinite {
int x;
struct infinite next;
}
5 List segments
A lot of the operations we’ll perform in the next few lectures are on segments
of lists: a series of nodes starting at start and ending at end.
start end
a1
a2
a3
a4
Recursively
For loop
While loop
start
data
next
18
end
if (p == x) {
// Oh, here it is!
return true;
}
i += 1;
}
On the ith iteration of our naive is_segment loop, we know that we can
get from start to p by following exactly i pointers. We know we have a
cycle if we can also get p by following fewer than i pointers. In our example
of a circular linked list, we could get to the list node containing 7 by follow-
ing either 1 next pointer or 4 next pointers, and we will return false the
second time we encounter that node.
Exercises
Exercise 1 We say “on the ith iteration of our naive is_segment loop, we know
that we can get from start to p by following exactly i pointers.” Write a function
is_reachable_in(list* start, list* end, int numsteps); this function
should return true if we can get from start to end in exactly numsteps steps.
Use this function as a loop invariant for is_segment.