*Pointers - PowerPoint PPT Presentation

1 / 87
About This Presentation
Title:

*Pointers

Description:

Intuitively, List is like a string of items with a marker pointing to ... Welcome to segmentation fault hell, or worse...code that's nigh-impossible to debug ... – PowerPoint PPT presentation

Number of Views:159
Avg rating:3.0/5.0
Slides: 88
Provided by: awo4
Category:
Tags: nigh | pointers

less

Transcript and Presenter's Notes

Title: *Pointers


1
Pointers
  • Annatala Wolf
  • 222 Lecture 7

2
List (a new container class)
  • Intuitively, List is like a string of items with
    a marker pointing to one of them.
  • Its similar to Sequence, except it isnt random
    access. You can only access at the location of
    the marker, but you can move the marker forward,
    or send it to the start or the end.
  • Mathematically, List is modeled by ordered pair
    of string of Item
  • The first string is the stuff before the marker,
    and the second string is the stuff after it.
  • A List remembers where its marker is!

3
Conceptualizing List
  • Heres a List of Text
  • (ltfoo, bargt, ltbaz, quuxgt)
  • Normally we think of List in a loose sort of
    string of Item way, but remember that the
    location of that fence between the two strings
    matters. The List above is not equal to the List
    below
  • (ltfoogt, ltbar, baz, quuxgt)
  • Thinking about the List as a pair of strings
    rather than, say, one string with an index into
    it, makes the specs much easier to read and
    understand.
  • It also makes it easier to visualize what its
    operations do.

4
List_Kernel Operations
  • Add_Right(x)
  • Remove_Right(x)
  • requires self.right gt 0
  • Accessor current
  • requires self.right gt 0
  • Advance( )
  • requires self.right gt 0
  • Move_To_Start( )
  • Move_To_Finish( )
  • Left_Length( )
  • Right_Length( )

All the action in a list will take place at the
location of the first item in the right
string. This makes Lists requires clauses
extremely simple! The only restriction is that
Remove_Right(x), current, and Advance() cant
be called when self.right is empty.
5
List Examples (Tracing Table)
6
Why List?
  • Why would you choose List instead of a more
    flexible component, like Sequence?
  • You might want to make sure you remember to
    access items in a list-like manner. Choosing a
    component that restricts how you can access data
    is a common safety measure in programming.
  • List might also be much simpler to implement
  • We might be able to get better performance from
    List.
  • List might be so natural to implement with
    pointers that it serves as a useful building
    block for other containers, such as Sequence.
    (As it turns out, this is the case.)

7
Non-layered Kernels
  • List, Queue, and Stack are very useful components
    because they are easy to implement using
    pointers.
  • Non-layered kernels (kernels written using only
    pointers and default Resolve types in Rep) are
    the best choice for the base component on which
    containers like Array, Partial_Map, Sequence,
    etc., are layered.
  • When we layer components, wed prefer the
    layering be shallow. If we restrict the number
    of levels of layers, we will improve
    performance.

8
Practice with List
  • As a client of List, write a layered extension
    for List that adds Retreat( ), the reverse of
    Advance( ).
  • procedure Retreat( )
  • /!
  • requires
  • self.left gt 0
  • ensures
  • self.left self.right
  • self.left self.right and
  • self.left self.left - 1
  • !/

9
Retreat( )
  • procedure_body Retreat( )
  • object Integer counter
  • counter self.Left_Length()
  • self.Move_To_Start()
  • while (counter gt 1)
  • self.Advance()
  • counter--
  • // What is the downside of implementing this as
    an
  • // extension, rather than a Kernel operations?

10
When Kernel is not enough
  • If we implement List_Retreat as a layered
    extension, it will run in linear time (bad)
  • This is because the Kernel operations dont give
    us what we need to meet the performance
    requirements we want
  • An alternative to this is to implement
    List_Retreat directly (well do this in Lab 5)
  • It will be just like implementing a Kernel (same
    restrictions), but well add a Retreat operation
  • Our List_Retreat wont depend on any other
    implementation of List_Kernel, naturally

11
A Few Pointers
xkcd.com
12
Data and Memory
  • Data is stored in memory
  • abstractly, thoughsystem may control how memory
    is actually allocated
  • Frequently, a large object will have all its
    memory allocated adjacently
  • In C, strings are implemented as array of char
  • For example, a string of characters might be

H
e
d
l
r
o
W
o
l
l
?
\0
13
Bytes and Storage
  • Each object in memory takes up a certain amount
    of spaceeven primitive values do
  • Java characters are 2 bytes each (based on old
    Unicode standard)
  • Older languages like C use 1 byte for char
  • Integer size depends on the language, and for
    non-standardized languages (like C) it can
    depend on the architecture of the machine as well
    (yes, this is a terrible annoyance)
  • Full object types like Integer or Character are
    often larger still

14
Addresses
  • Every location in memory (even virtual memory)
    has a numeric address
  • data memory addr. belongings home addr.
  • Theres stuff at every location, whether youre
    using it or not
  • Often, leftover garbage from previous use

100
102
120
118
116
114
112
108
106
104
110
122
124
126
096
098
128
H
e
d
l
r
o
W
o
l
l
?
\0
_at_
g
q

d
15
Binary Storage
  • At the lowest level, everything is stored
    digitally, as individual bits
  • Heres the same string only with numeric
    versions of the values inside

100
102
120
118
116
114
112
108
106
104
110
122
124
126
096
098
128
72
101
100
108
114
111
87
111
108
108
32
0
64
103
113
38
100
16
Limitations of Static Memory
  • Say you need to write a program that takes
    Integer input from a user over and over until the
    user enters the number -123, then prints all of
    those Integers back in sorted order (by ).
  • Assume you dont have access to any container
    objects, like Array or Set or Sorting_Machine
  • input gtgt num
  • while (num ! -123)
  • object Integer num2
  • input gtgt num2
  • // wait... now what?
  • // num2 disappears each loop...

17
Bad Solutions
  • We cant just make a variable in a while loop
    because that variable will disappear each loop.
  • We cant make 100,000 variables what if we need
    100,001?
  • We could do this by using recursion. But imagine
    trying to sort the values with a recursive
    operation that can only access one or two values
    at a time!
  • It can be done, but its not intuitive, and its
    not the way we typically use recursion in C or
    Java.
  • Functional programming languages, like LISP, do
    use this approach to access dynamic memory.

18
Dynamic Memory
  • What we need is dynamic memory thats simpler to
    use than recursion. Dynamic means we wont know
    how much memory we need until the program has
    been running a while. There are several possible
    cases
  • the program relies on outside input, which is
    unknown
  • the algorithm is solving some problem that needs
    to run before we know how much memory it will
    need to finish
  • its more convenient (in some cases)
  • In procedural programming languages like Java and
    C, dynamic memory is accessed in two ways
  • implicitly through recursion
  • explicitly by using pointers

19
How Dynamic Memory Works
  • You already understand how recursion works!
  • Pointers do it by separating the place a variable
    is declared from the place its object is created
  • This lets us define objects that are capable of
    linking to other objects of the same type,
    without recursively creating 8 objects. We can
    create exactly the number we need, when needed,
    in a linked list.
  • It allows us to dynamically create a large array
    of objects all at the same time, where we decide
    at runtime how many objects we need.
  • It allows polymorphism to decouple dependencies.
  • ( these are not done in Resolve, but well
    discuss them later)

20
Scope of Static Objects
  • Static objects are allocated on the program
    stack. Every time you hit an open brace, all the
    objects declared inside that scope are created.
  • When you hit a closing brace, all those objects
    go out of scope (cease to exist).
  • if (blah)
  • object Integer i
  • Print(i)

Program Stack
i 0
sub()
This frame in the stack (and its variables
and objects) will disappear at the closing brace.
main()
21
You Have Been Spoiled
  • When you create a stack-based object, youre
    really doing a bunch of things at once. But you
    need to understand that the things youre doing
    are separate concepts.
  • Heres what actually happens when the compiler
    runs
  • object Foo bar
  • create a new variable (a name for an object)
    called bar
  • set the type of bar to Foo (for safety, and for
    instance operations)
  • then, allocate memory on the stack big enough to
    hold a Foo
  • create a new Foo object in that space
  • run the Resolve constructor to initialize the new
    Foo object
  • This happens for all objects declared in a
    particular scope when the preceding is
    encountered (while code is running).

22
The Difference!
  • Static, stack-based objects vs. pointers
  • Declaring a pointer gives you a variable, but
    doesnt create an object until you tell it to!
    Initially the pointer variable is dangling it
    doesnt name a real object, and if you try to use
    it like it has an object, its a (serious) error.
  • If your pointer goes out of scope and is tied to
    an object, that object does not go out of scope
    (it persists after the pointer is gone). This
    can also lead to serious errors.
  • Pointers generally allocate memory on the heap,
    not the program stack.
  • In C, you can allocate a dynamic, primitive
    array with a pointer (but we dont do this in
    Resolve).

23
Pointers
  • Essentially, a pointer is just a number. Its
    usually represented as an unsigned integer.
  • The number refers to a location in memory, and
    can be used to get the value at that location.

If the value at address 128129 is a
pointer, it references the value living at
address 100.
100
128
72
101
100
108
114
111
87
111
108
108
32
0
64
103
113
38
100
24
Pointer Types
  • Pointers are a parameterized type. They need to
    know the type of the objects they can point to,
    for two reasons
  • Type safety so you can use the object correctly
  • Size so the computer can allocate the right
    amount of memory (and make dynamic arrays)

If the value at 128129 is a Foo pointer, and a
Foo objects Rep needs 2 bytes, then the pointer
currently refers to 100101 when that value is
treated like a Foo.
Each box here means 2 bytes.
100
128
72
101
100
108
114
111
87
111
108
108
32
0
64
103
113
38
100
25
The Heap
  • The word heap means several things in computer
    science. In the context of memory, it refers to
    the place in memory where objects are created
    dynamically (not counting how you can use
    recursion on the program stack to get memory).
  • Theres not usually a cutoff between the stack
    and the heap. Generally the stack grows up, the
    heap grows down, and when they meet youre out of
    memory (segmentation fault you tried to put a
    stack variable into the heap segment or vice
    versa).

The Heap
Big object!
memory not in use (random crap here)
(tiny object)
i 0
no wasted memory down here
sub()
main()
Program Stack
26
Pointers? Fear Not!
  • Many people consider this to be the hardest part
    of 222
  • But pointers are easy! (LIFE is hard.)

xkcd.com
27
Pointer Terminology
  • alias when two pointers (both alive, generally)
    are referencing the same object (so one object
    has multiple names)
  • alive, valid a pointer that is currently
    pointing at a valid object
  • bad, dangling, dead, or wild a pointer that is
    not NULL, but is not pointing at a valid object
    (this is dangerous)
  • dereference to get the contents of what a
    pointer is pointing to (only works if its alive)
  • reference another name for a pointer, or what a
    pointer does (it references an object) in C
    this often means a living, aliased pointer you
    cant reassign that acts like its not a pointer
  • This is secretly the way that passing by
    reference works! It passes a copy of a pointer
    to the object, but acts like its a stack-based
    object, and dereferences the pointer
    automatically when used. So if you modify the
    object via the alias, it modifies the original.

28
Minor Differences
  • The terms bad, dangling, dead, and wild are all
    non-standard. They can be used interchangeably,
    but the following terminology is most common
  • The only official terminology is valid pointer
    for living pointers (though alive is very
    common), and invalid pointer for everything else
    (meaning NULL, dangling, or wild pointers).
  • A dangling pointer is a pointer after its object
    is deleted.
  • A wild pointer is a pointers initial value (only
    in languages where pointers receive no default
    initial value).
  • Some authors use either of the terms dangling or
    wild to mean both dangling and wild pointers (our
    book does this).
  • Dead usually means dangling, but could mean
    invalid, wild, or both dangling and wild.
  • Bad has no standard meaning. It usually means
    invalid.

29
Pointer_C(the safer, checked version of
Pointer)
  • Intuitively, modeled by pointer to Item
  • No default value! Pointers begin wild.
  • Instantiated as Pointer_CltItemgt
  • Dont instantiate it as a class. Instead, inline
    the template directly into your declaration
  • object Pointer_CltFoogt fptr
  • Pointers support
  • p q assignment (set the value of the
    pointer)
  • p q (or !) equality testing (test for
    aliasing)
  • p dereferencing (access the object it points
    to)
  • (If p is read pee, I generally read p
    like star-pee.)

30
Global Pointer Operations
  • New(ptr) dynamic memory allocation obj. create
  • allocates space for new object of type Item
  • creates new object of type Item in that space
  • stores the address of this new Item in ptr
  • Delete(ptr) dynamic memory cleanup obj. destroy
  • destroys the object (runs its destructor,
    Finalize, etc.)
  • flags memory used by the Item at ptr for deletion
  • this allows the memory to be reused later
  • ptr is now dangling (it no longer points at a
    valid object)
  • NULL a special constant pointers can be set to
  • if we set a pointer to NULL, we can tell that
    its not alive, for safety
  • but if a pointer isnt NULL, it can still be
    dangling / wild

31
Manipulating Pointers
  • The operator copies the value of one pointer
    into another pointer. It can copy NULL, or a
    dangling value, or it aliases one pointer to
    anothers object.
  • The dereference operator (p) returns a reference
    to the object the pointer points to, but only if
    p is a living pointer. Otherwise, its a
    dereference error.
  • The operator tests to see if two pointers are
    aliased (you can also test if a pointer NULL)
  • p q true iff the pointers refer to the same
    object
  • p q true iff the objects the pointers refer
    to are equal to each other, but not necessarily
    the same object (only possible if the type of p
    has operator)

32
Using New and Delete
  • With a stack-based object, the object is created
    when the opening brace is encountered, and
    destroyed when the closing brace is encountered.
  • A heap-based object is created when New() is
    called on a pointer, and destroyed when Delete()
    is called on any pointer that currently points to
    that object.
  • If a pointer is destroyed (a pointer on the stack
    goes out of scope, or a pointer on the heap is
    Deleted), this does not delete the object the
    pointer points to!
  • Just keep in mind the syntax for deleting the
    object p is Delete(p), not Delete(p), since you
    can only Delete things by using a pointer to
    them. New() works the same way.

33
Why Delete?
  • Delete is necessary because its the only way the
    computer knows youre done using dynamic memory.
  • Java doesnt need to delete objects because it
    uses smart pointers that keep track of how many
    references exist. When that number drops to
    zero, Java flags the object for garbage
    collection.
  • A small loss of performance is exchanged for
    safety.
  • In C, however, if you fail to Delete something
    and lose the last reference to it, you have a
    memory leak a region of memory you can no longer
    access until the program terminates and the OS
    frees up the memory.
  • If a memory leak happens repeatedly, it can crash
    your program because youll run out of memory.

34
How To Think About Pointers
  • Every time you say object Foo bar, you create a
    new name for an object, and you also create a new
    object at the same time. Those two are then
    stuck togetheryou always use that name to refer
    to that object (unless you pass it by reference,
    but that uses pointers).
  • With pointers, you create the name first. You
    can create the object later, or even attach the
    name to a different object that already exists
    (thus giving it more than one name an alias).
  • When youre done using an object, you have to
    destroy the object to get the memory back.

35
Using Pointers
  • // Create a name for a Foo object, called baz.
  • // Unlike a stack-based object, this does not
    create a
  • // new Foo object (yet). Currently, baz is wild!
  • object Pointer_CltFoogt baz
  • // Set the value of baz (that is, the place it
    points to)
  • // to the value of quux. Now they point to the
    same
  • // place, and if quux is alive, they alias the
    same object.
  • baz quux
  • // Create a new object and attach baz to that
    object. Now
  • // it must be true that baz is no longer is
    aliased to quux.
  • New(baz)
  • // You use (baz) like you would a stack-based
    Foo object.
  • (baz).FooProcedure() // baz is alive, so
    use star-baz

36
Pointers In Resolve/C
  • To limit pointer errors, we only use pointers
    inside Kernels and non-layered extensions (that
    is, at the lowest level).
  • When we use pointers in this class, they will
    usually point to a Record of some sort.
    Unfortunately, binds later than . This
    means the computer always tries to run before
    , and you have to use parenthesis to get the
    order right.
  • object Pointer_CltNodegt p // new pointer on
    stack
  • New(p) // new Node record
    on heap
  • // dereference p, then use to get ps Nodes
    data field
  • (p)data x
  • // Error! Tries to use on a pointer. (Which
    is silly.)
  • // pdata x

37
Pointer Pictures
  • I use the following conventions for pointers in
    this class
  • Pointers are triangles with arrows coming out of
    them
  • The value of a pointer is where the arrowhead
    points
  • NULL pointers, dangling pointers, etc. see
    illustrations

3
p
a known-to-be- dangling pointer
a pointer whose value is unknown
3
q
p, q, and r are living pointers to Integer
objects. Notice p q p q p r
p ! r
a NULL pointer (poor flaccid guy)
r
38
Nodes
  • A common use for pointers is to create some
    arbitrarily big recursive structure, like a
    string or a tree. Think of it like data arranged
    in a graph
  • Each node in the graph holds
  • a piece of the data (an Item)
  • one or more pointers to other nodes
  • // example Node fields from Stack_Kernel_1
  • field_name (Node, 0, Item, data)
  • field_name (Node, 1, Pointer_C ltNodegt, next)

3
4
8
0
(Node) RecordltItem, Pointer_CltNodegtgt
(Item)
data
(Pointer_CltNodegt)
next
39
Linked Lists
  • A linked list is a common data structure used
    to hold a dynamic amount of information.
  • Each node in a linked list contains a pointer to
    the next node in the list.
  • Inserting a new node into a linked list takes
    constant time, but finding the location is linear.

data field
the rest of the linked list
next field (pointer to the next node)
first node
second node
40
Example Using Linked Lists
  • /! requires first pts to first node in a linked
    list
  • ensures x added into a new node, to front
    of list,
  • and first is updated
    !/
  • procedure Prepend (Pointer_CltNodegt first, Item
    x)
  • // make a new node and stick x into it
  • object Pointer_CltNodegt insert
  • New(insert)
  • (insert)data x // consumes x !
  • // stick insert into the list and update
    first
  • (insert)next first
  • first insert

41
Contract
  • requires first pts to first node in a linked
    list
  • ensures x added into a new node, to front of
    list,
  • and first is updated

Our job is to make a new Node for x, link the
Node to the front of this linked list, and
finally update first.
A linked list must have at least one node in it,
so we know first is alive.
first(Pointer_CltNodegt,passed by reference)

This could be alive, dangling, or NULL. (We
dont know the length of the linked list, or
even what convention may apply.)
?
?
x(Item, passedby reference)
(first)(must exist)
42
Make a Pointer (on the stack)
  • object Pointer_CltNodegt insert

passed in by referencesecretly is a safe
pointer to matching argument could be on stack
or heap
somewhere on the heap
the top of the program stack
insert
first

?
x
(first)
43
Make a New Node
  • New(insert)

passed in by reference
somewhere on the heap
the top of the program stack
Ø
(default values)
insert
first
(insert)

?
x
(first)
44
Swap in the Data
  • (insert)data x

passed in by reference
somewhere on the heap
the top of the program stack

ltOM NOM NOM SWAPgt
insert
first
(insert)
Ø
?
x
(first)
Consumed!
45
Link New Node to First Node
  • (insert)next first

passed in by reference
somewhere on the heap
the top of the program stack

insert
first
(insert)
Ø
?
x
A new name (alias) for this Node object!
(first)--- or ---((insert)next)
46
Update first Parameter
  • first insert

passed in by reference
somewhere on the heap
the top of the program stack

Aliased!
insert
first
(insert)--- or ---(first)
A new name (alias) for this Node object!
Ø
?
x
This Node object lost the name (first), but
gained the name ((first)next).
((insert)next)--- or ---((first)next)
47
Finish the Operation

passed in by reference
somewhere on the heap
the top of the program stack

We can reach everything no memory leaks.
insert
first
(first) (now, its only name)
Gone out of scope! The pointer gets deleted, but
its object is fine. This is not the same as
Delete(insert), which would delete the object.
Ø
?
(Still dunno where this guy points...)
x
((first)next) (now, its only name)
48
Linked List Conventions
  • To use a linked list, you will need a pointer to
    the first node so you can access it (unless the
    first node is on the stack, which is uncommon).
    In Resolve, it will typically be part of the Rep.

Another way to tell when a list ends is by using
a convention, like the last pointer in the list
is NULL.
(Rep) RecordltPointer_CltNodegt, Integergt
(Pointer_CltNodegt)
(selftop)
top
(Integer)
One way to tell when a list ends is by caching
the lists length.
2
((selftop)next)
length
self
49
Traversing Linked Lists
  • To traverse a linked list, youll want to make
    use of aliasing. Its often a lot easier to use
    an alias than to write a really long expression,
    and sometimes you need to use one.
  • You have to hold on to all the data with
    something, at all times. If you ever lose all
    pointers to some part of the list, you have a
    memory leak. ?
  • // make a copy of the pointer to the first node
  • object Pointer_CltNodegt alias selffirst
  • // then move the pointer to the next node
  • alias (alias)next

50
Visualizing Traversal
p (p)next
(p)next
p
p
This works even if the next node doesnt exist
(but the current node must exist). If its the
last node in the list, p now contains what the
last node points to (NULL or dangling).
To get to the next node, we alias our traversing
pointer to the next node in the list.
51
Pointer Practice!Node RecordltItem data,
Pointer_CltNodegt nextgt
Get into groups! Use an alias to traverse the
list.
  • local_function Item Get_Item (
  • preserves Pointer_CltNodegt first,
  • preserves Integer index )
  • /!
  • requires
  • index gt 0 and
  • first points to the first node of a
    singly-linked list containing at
    least (index 1) nodes
  • ensures
  • Get_Item reference to the data field
    of the (index 1th) node in the
    linked list, starting from first
  • !/

(Pointers are easy to pass by copy.)
52
Get_Item
  • local_function Item Get_Item ( preserves
    Pointer_CltNodegt
    first, preserves Integer index)
  • // make an pointer and alias it to first node
    in list
  • object Pointer_CltNodegt search first
  • // advance the alias through the list until
    we hit index
  • while (index gt 0)
  • search (search)next
  • index-- // passed by copy, so ok
  • // search is now an alias for the node at
    index
  • return (search)data

53
Finalize
  • Destructors must clean up all of the memory
    allocated by an object over its lifetime.
  • Rep does this for you automatically for its
    fields.
  • Just before Rep does this, the Finalize( )
    operation (if it exists) is called. Its only
    job is to Delete anything that was allocated.
  • If you allocate memory dynamically, you will need
    to implement Finalize() or your objects will leak
    like a sieve.

54
Sequence_Kernel_1_Naïve
  • Representation
  • selffirst Pointer_CltNodegt
  • selflength Integer
  • Correspondence
  • if self.length 0 ltgt
  • else the data fields in the singly-linked list
    that begins with (self.first), in order
  • Convention
  • self.length gt 0, and if self.length gt 0 then
    self.first points to a singly linked list with
    self.length nodes

Node is a Record Item data P_CltNodegt
next
55
SK1NRemove(pos, x)
  • procedure Remove(preserves Integer pos, produces
    Item x)
  • object Pointer_CltNodegt target selffirst,
    before NULL
  • while (pos gt 0)
  • before target
  • target (target)next
  • pos--
  • object Pointer_CltNodegt after
    (target)next
  • (target)data x
  • if (before NULL)
  • selffirst after
  • else
  • (before)next after
  • Delete(target)
  • selflength--

When we remove a node, to patch the list, we will
need to fix the node that points to it. We need
to keep track of the previous node to fix it.
If we just removed the first node, we have to fix
selffirst instead!
Otherwise, just link the previous node up with
the next.
56
Practice! SK1NAdd(pos, x)
  • Node next, data
  • Rep first, length
  • no null convention
  • length number of nodes in linked list
  • Write Add(pos, x) for Sequence
  • hint you need to consider two cases, and when
    walking through the list you need to stop one
    step before where youll be adding the item
  • you will probably want to make two pointers

Get into groups of 3-5!
57
SK1NAdd (pos, x)
  • procedure Add(preserves Integer pos, consumes
    Item x)
  • object Pointer_CltNodegt before, insert, after
    selffirst
  • New(insert)
  • (insert)data x
  • object Integer afterIndex
  • while (afterIndex lt pos)
  • before after
  • after (after)next
  • afterIndex
  • (insert)next after
  • if (pos 0)
  • selffirst insert
  • else
  • (before)next insert

Create aliases, make new node, consume x.
Move through list keep track of nodes before and
after the planned insertion.
If first node, update first
else update node before.
58
Other Approaches
  • There are other possible plans of attack none is
    superior. Aim for clarity.
  • If long expressions make sense to you, use them.
    If more aliases seem to make things simpler, use
    aliases.

object Pointer_CltNodegt second second
(selffirst)next return (second)data //
equivalent // return ((selffirst)next)data

59
The Special Case
  • The reason I say Sequence_Kernel_1_Naïve is
    that this model differs slightly from the actual
    Sequence_Kernel_1. Specifically, SK1 uses a
    trick to remove the special case.
  • The reason we need a special case in SK1 is that
    one of the pointers to data nodes in the list is
    not in the linked list itself. Namely,
    selffirst must be updated if we change the
    first node.
  • While this is hardly a major inconvenience now,
    if the linked list is a little more complicated,
    it can become a real headache.

60
Sentinel Node
  • A sentinel node (or dummy node) is a node used to
    mark a boundary, rather than to hold data.
  • The basic idea if we include an extra node at
    the front of the list that doesnt hold data, we
    can generalize the algorithm so we no longer need
    to treat the first node as a special case.

This data field is unused.
This is the data at index 0.
pre_front
?
(remainder of list)
length
sentinel node
regular data node(index 0)
self
61
Terminology Note
  • The book refers to sentinel nodes as smart
    nodes to make a point that the nodes are useful
    lest you mistakenly interpret the term dummy as
    stupid rather than placeholder.
  • Please be aware that this is not typically what
    the term smart means in programming parlance.
    Smart usually means an enhanced or
    self-maintaining version of an object.
  • For example, Pointer_C is a smart pointer.

62
Add() Using Sentinel Node
  • procedure Add(preserves Integer pos, consumes
    Item x)
  • object Pointer_CltNodegt before
    selfpre_first, insert
  • object Pointer_CltNodegt after
    (before)next
  • New(insert)
  • (insert)data x
  • object Integer afterIndex
  • while (afterIndex lt pos)
  • before after
  • after (after)next
  • afterIndex
  • (insert)next after
  • (before)next insert
  • selflength

This time, the node before the insert must also
be in the list (selfpre_first is a sentinel).
Move through list, just as before. All of the
pointers to data nodes are now contained in the
linked list.
Now theres only one case to fix!
63
List_Kernel_1
In LK1, we use a sentinel node at the start of a
singly-linked list. There are (left_length
right_length 1) nodes in the list. The last
node in the linked list is pointed to by
finish. The node that holds the last data item
in the Lists left string is pointed to by
last_left (its equal to pre_start if left is
empty).
pre_start
last_left
Get into groups of 2-4 and write Initialize.
finish
?
4
left_length
sentinel node
regular data node
right_length
here, abstract self (ltgt, lt4gt)
self
64
private LK1Initialize()
  • local_procedure_body Initialize()
  • New(selfpre_start)
  • selflast_left selfpre_start
  • selffinish selfpre_start

65
Thinking about LK1
  • The sentinel node eliminates some special cases,
    but not all of them.
  • For example, in both Add_Right(x) and
    Remove_Right(x), there is a special case when you
    need to update selffinish. This is because
    theres no sentinel node at the end in LK1.
  • If you add or remove the last node from the
    linked-list, you have to update selffinish.

66
Doubly-linked List
(Node)
  • A doubly-linked list is similar to a
    singly-linked list, except you can traverse the
    list in both directions.

data
next
previous
The price we pay for this flexibility (apart from
the obvious tiny increase in space for extra
pointers) is a small amount of extra maintenance.
67
List_Retreat_2
  • In Lab 5 your job is to write the component
    List_Retreat_2. Its very similar to
    List_Kernel_1, except
  • Its a non-layered extension, so in addition to
    implementing all eight List operations, you need
    to implement Retreat().
  • It uses a doubly-linked list to hold the data!
  • It uses two sentinel nodes one at the beginning,
    and one at the end. (This actually makes the
    algorithms much easier!)

68
List_Retreat_2
  • For List_Retreat_2 (in Lab 5), the value of Rep
    for an empty list should be a doubly-linked list,
    something like this picture.

pre_start
last_left
Having two sentinel nodes will eliminate more
special cases, which makes it easier to write the
algorithms.
post_finish
0
?
?
left_length
0
right_length
self
69
Practice Doubly-Linked ListsGet into groups of
3-5!
  • local_procedure Remove (
  • produces Item x, preserves Pointer_CltNodegt
    target
  • )
  • /!
  • requires
  • there exists i, j Integer where
  • (i gt 1 and
  • j gt i and
  • target points to the ith node
    of a
  • doubly-linked list containing j
    nodes)
  • ensures
  • x target.data and targets Node
    is deleted
  • and the doubly-linked list is
    repaired
  • !/

70
Remove(x, target)
  • local_procedure Remove (
  • produces Item x, preserves Pointer_CltNodegt
    target
  • )
  • x (target)data
  • object Pointer_CltNodegt left
    (target)previous
  • object Pointer_CltNodegt right
    (target)next
  • (left)next right
  • (right)previous left
  • Delete(target)

These aliases arent necessary, but they make the
code much easier to write and follow.
71
State of a Pointer
  • A variable can be in one of four states
  • Alive / Valid (refers to an actual object)
  • Any kind of variable
  • Out of scope (no longer available to use)
  • Any kind of variable
  • NULL (flagged, so you know its invalid)
  • Java references, C/C pointers
  • Bad / Dangling / Dead / Wild (dangerous)
  • C pointers only

72
NULL At least it warns you!
  • If you know a pointer is NULL, you know its
    badso using NULL to flag bad pointers is a
    common convention for avoiding errors.

Frisby (omgfrisby.com)
73
Pointer Reference Errors
  • Aliasing error when two references refer to the
    same object, but you act like theyre two
    objects.
  • If you change one, the other changes too. In
    some circumstances this can be extremely
    dangerous.
  • To avoid aliasing errors, limit aliasing to short
    sections of code (local scope).
  • Dereferencing NULL just what it says.
  • It will stop the program if not caught, but is
    not dangerous.
  • You can catch this at runtime with exception
    handling.
  • Deleting NULL not an error!
  • The program just ignores this command.

74
Pointer Errors (Serious)Generally found in C,
C, C
  • Memory Leak youre losing (leaking) available
    memory (eventually this will cause a crash).
  • You can no longer access an object you created on
    the heap.
  • If you drop the last pointer to an object on the
    heap, you cant ever free that memory.
  • Using invalid pointers when you dereference or
    delete a non-NULL, invalid pointer.
  • You will end up changing memory in arbitrary
    ways.
  • These errors are not only dangerous, they can be
    truly impossible to debug!

75
When Errors Arent
  • When working with RESOLVE/C without pointers,
    these problems are easily avoided.
  • Pass-by-reference is fine, because the incoming
    reference is always alive and the pointer
    cant be reassigned to something bad.
  • The swapping paradigm avoids much of the need for
    handling pointers directly, as does the use of
    templates instead of polymorphism.
  • RESOLVE discipline always encapsulates pointers
    at the Kernel level, so if your Kernels work, you
    wont ever get pointer errors.

76
Life of a Stack-based Object
Stack-based objects are deleted when you leave
the scope in which they were declared/created.
All variables you declare on the stack are
alive once you declare them (or, for
parameters, once the operation starts).
77
Life of a Java Reference
Memory leaks cannot result when a living
reference changes Java keeps track of objects
and deletes for you automatically.
Dereferencing a null pointer is the only
reference-type error possible in Java (and its
catchable).
Java references (which are pointers) start with a
default value of null. They cant be explicitly
deleted, so they are never dangling.
If a reference isnt a data member, Java code
wont compile unless you assign it a value before
using it.
78
Life of a C/C Pointer
Dereferencing NULL is catchable, fortunately.
If you reassign a living pointer, or it goes out
of scope, and it was the last reference to its
object this is a memory leak.
When a pointers object is deleted, all aliases
equal to that pointer also change state from
alive to dangling, spontaneously.
If you use an invalid pointer as though it were
alive this is an invalid pointer error.
These two errors are extremely dangerous!
79
Spontaneous Transition
  • The green transition illustrates a situation that
    may lead to serious errors.
  • Any time a pointers object is deleted, the
    object is no longer safe to use. Consider

?
Delete(p)
Now what happens?

p
p
?
q
q
80
Dangers of Careless Aliasing
  • When Delete is called on pointer foo, all
    pointers aliased to foo spontaneously die, even
    though no code mentions these pointers
    explicitly!
  • It is possible for the status of a pointer to
    change from alive to dangling, even when no code
    refers to that pointer.
  • This is one reason aliasing is best kept to local
    scope.

Delete(p)
Now, both p and q are dangling!

p
p
q
q
81
Study Note
  • The remaining slides in this lecture are useful
    practice for thinking about pointers, but you
    wont be tested on the contents.
  • So dont study them too hard. ?

82
Other Graph Structures
  • Its possible to use pointers to allocate memory
    dynamically in any sort of directed graph
    structure, not just a linked list.
  • For example, we could easily use pointers to
    create a node-based binary tree structure

83
Object-Based Pointers
Rep for Binary_Tree_Kernel_1
  • Another approach would be to use a Rep that
    includes one or more pointers to the objects own
    type (rather than to a primitive Node).
  • If the object is a recursively-defined structure
    where each child should be able to do all the
    stuff the parent object should, this makes sense.

data
left_subtree_ptr
right_subtree_ptr
size
height
self
84
Binary_Tree_Kernel_1
  • correspondence
  • if self.size 0 self empty tree
  • else self (self.data root,
    self.left_subtree_ptr left_subtree,
    self.right_subtree_ptr right_subtree)
  • convention
  • if self.size 0 both pointers NULL and
    self.data is its default value
  • else both pointers are alive
  • self.size self
  • self.height HEIGHT(self)
  • Get into groups of 3-5 and write
  • Initialize()
  • Finalize()
  • Compose(x, left, right)
  • Decompose(x, left, right)

Rep for Binary_Tree_Kernel_1
data
left_subtree_ptr
right_subtree_ptr
size
You may want to start Compose() with
self.Clear(), and two New()s.
height
self
85
BTK1 Local Operations
  • procedure_body Initialize ()
  • selfleft_subtree_ptr NULL
  • selfright_subtree_ptr NULL
  • procedure_body Finalize ()
  • Delete(left_subtree_ptr)
  • Delete(right_subtree_ptr)

Why dont we need to iterate through the tree to
free everything up? Because the pointers here
are to objects, not simple Nodes. (When deleted,
each subtree runs Finalize() and calls Delete on
its subtrees!)
Remember, a call to Delete using a NULL pointer
will just end up being ignored.
86
BTK1Compose
  • procedure_body Compose (
  • consumes Item x,
  • consumes Subtree_Type left_subtree,
  • consumes Subtree_Type right_subtree
  • )
  • self.Clear()
  • selfdata x
  • New(selfleft_subtree_ptr)
  • New(selfright_subtree_ptr)
  • selfsize 1 left_subtree.Size()
    right_subtree.Size()
  • if (left_subtree.Height() gt
    right_subtree.Height())
  • selfheight left_subtree.Height() 1
  • else
  • selfheight right_subtree.Height()
    1
  • ((selfleft_subtree_ptr)) left_subtree
  • ((selfright_subtree_ptr))
    right_subtree

This will ensure x gets consumed, and the old
subtrees get deleted.
Swap will work as long as our tree pointers are
valid.
87
BTK1Decompose
  • procedure_body Decompose (
  • produces Item x,
  • produces Subtree_Type left_subtree,
  • produces Subtree_Type right_subtree
  • )
  • selfdata x
  • ((selfleft_subtree_ptr)) left_subtree
  • ((selfright_subtree_ptr)) right_subtree
  • self.Clear()

This is the fast way to clear selfdata and set
both pointers to NULL.
Write a Comment
User Comments (0)
About PowerShow.com