Professional Documents
Culture Documents
Pitfall: 230 Chapter 5 / Linked Lists
Pitfall: 230 Chapter 5 / Linked Lists
When both a const and a non-const version of a function are present, the com-
piler automatically chooses the correct version, depending on whether the func-
tion was activated by a constant node (such as const node *c ) or by an
ordinary node.
P I T FALL
DEREFERENCING THE NULL POINTER
One of the most common pointer errors is writing the expression *p or p-> when
the value of the pointer p is the null pointer. This must always be avoided because
the null pointer does not point to anything. Therefore, when p is the null pointer, *p
(which means “the thing that p points to”) is meaningless. In this case, p-> is also
meaningless. Because the asterisk in *p is called the dereferencing operator, we
can state this rule: Never dereference the null pointer.
Accidental dereferencing of the null pointer is sometimes a hard error to track
down. The error does not cause a syntax error. Instead, when the program is
running, there will be an attempt to interpret the null pointer as if it were a valid
address. Sometimes this causes an immediate run-time error with a message such
as “Address protection violation” or “Bus error, core dumped.” But on other
machines, the null address might be a valid address, causing your program to read
or write an unintended memory location. Often this memory location is part of the
machine’s operating system, resulting in part of the operating system being cor-
rupted. At some later point, perhaps after your program has completed, the cor-
rupted operating system can cause an error. Fortunately, restarting your machine
usually writes a fresh copy of the operating system into memory—but even so,
never dereference the null pointer!
We’re now in a position to design container classes that use linked lists to store
their items. The member functions of the container class will put things into the
linked list and take them out. This use of a linked list is similar to our previous
use of an array in a container class. However, you may find that storing and
retrieving items from a linked list is more work than using an array because we
don’t have the handy indexing mechanism (such as data[i]) to read or write
elements. Instead, the class requires extra functions just to build and manipulate
the lists—parts that are not central to the container’s main objectives. a collection of
In fact, many container classes might need these same extra functions, which linked-list
suggests that we should write a collection of linked-list functions once and for functions
all, allowing any programmer to use the functions in the implementation of a
container class. This is what we will do, creating a small toolkit of fundamental
linked-list functions. The primary purpose of the toolkit is to allow a container
class to store elements in a linked list with a simplicity and clarity that is similar
to using an array. In addition, having the functions written and thoroughly tested
once will allow us to use the functions to implement many different container
classes with high confidence in their reliability.
258 Chapter 5 / Linked Lists
CHAPTER SUMMARY
• A linked list consists of nodes; each node contains some data and a
pointer to the next node in the list. The pointer field of the final node con-
tains the null pointer.
• Typically, a linked list is accessed through a head pointer that points to
the head node (i.e., the first node). Sometimes a linked list is accessed
elsewhere, such as through the tail pointer that points to the last node.
• You should be familiar with our functions to manipulate a linked list.
These functions follow basic patterns that every programmer uses. Such
functions are not node member functions (so that they can handle empty
lists with no nodes).
• Linked lists can be used to implement a class. Such a class has one or
more private member variables that are pointers to nodes in a linked list.
The member functions of the class use the linked-list functions to manip-
ulate the linked list, which is accessed through private member variables.
• You have seen two classes implemented with the linked-list toolkit: a bag
and a list. You will see more in the chapters that follow.
• A doubly linked list has nodes with two pointers: one to the next node and
one to the previous node. Doubly linked lists are a good choice for sup-
porting a cursor that moves forward and backward.
• Classes can often be implemented in many different ways, such as by
using a dynamic array or using a linked list. In general, arrays are better at
random access; linked lists are better at insertions/removals at a cursor. In
the STL, the vector uses a dynamic array and the list uses a linked list. A
third choice, the STL deque, uses a mechanism that we’ll see later.
9. The portions of the operating system that are you implement it from Exercise 24), and
currently in memory can be overwritten. list_head_insert. The delete operator is
used in the functions list_head_remove,
10. The implementation will compile correctly. list_remove, and list_clear.
But since the return value is a pointer to a
node in the list, a programmer could use the 20. Never call delete unless you are actually
return value to change the linked list. In gen- reducing the number of nodes.
eral, the return value from a constant member
function should never allow the underlying 21. Using functions from Section 5.2:
if (head_ptr != NULL)
linked list to be changed.
{
11. Change the return type to const node*, and if (head_ptr->link() == NULL)
provide a second non-const function that list_head_remove(head_ptr);
returns the link as an ordinary node*. else
list_remove(head_ptr);
12. We need only one function to access the data }
field because this function returns a copy of
the data (and it is not possible to change the 22. It will be the null pointer.
underlying linked list by having merely a copy
of the data from a node). 23. The one line will be:
previous_ptr->set_link
13. The #include <string> directive must be (new node
added to the other include directives in the (entry, previous_ptr->link())
toolkit’s header file. Then we can change the );
typedef statement to:
24. The implementation is nearly the same as
typedef string value_type;
list_copy, but the copying must stop when
14. for ( the end node has been copied.
cursor = head_ptr;
cursor != NULL; 25. Here is the const version:
cursor = cursor -> link( ) const node* list_locate(
) const node* head_ptr,
{...} size_t position
)
15. A node pointer should be a value parameter // Library facilities used: cassert, cstdlib
when the function accesses and possibly mod- {
ifies a linked list, but does not need to make const node *cursor;
the pointer point to a new node. size_t i;
16. locate_ptr = locate_ptr->link( ); assert(0 < position);
If locate_ptr is already pointing to the last cursor = head_ptr;
node before this assignment statement, then for (
the assignment will set locate_ptr to the i = 1;
null pointer. (i < position)
&&
17. Using functions from Section 5.2: (cursor != NULL);
if (head_ptr == NULL) ++i
list_head_insert(head_ptr, 42); )
else cursor = cursor->link();
list_insert(head_ptr, 42);
return cursor;
18. if (head_ptr->link( )==NULL) }
tail_ptr = head_ptr;
26. The definition makes bag::value_type the
19. The new operator is used in the functions same as node::value_type, so that a pro-
list_insert, list_copy, list_piece (if grammer can use bag::value_type without