J
jacob navia
This group has lost many people. One of the reasons is that the
newcomers just ask a question or two, and do not see discussions
that try to explain code, teaching newcomers to the language how
a program works.
This is a second example (after hexdump.c) of commented code,
i.e. explaining step by step how a relatively simple operation
is done.
Reversing a linked list
-----------------------
This is an evergreen of data structures programming. In most classes
you will get an exercise like this:
Exercise 7: Given a list "L" reverse it without moving its contents.
The solution for this exercise is to go through the list, relinking the
"Next" pointers in the inverse order, without touching any data that
the list may hold. We will use the code of the C containers library and
study how it is done.
The library uses a simple structure "ListElement" whose definition runs
like this:
typedef struct _ListElement {
struct _ListElement *Next;
char Data[];
} ListElement;
We have a pointer to the next element of the list, and a chunk of data
of unspecified length. This is the basic structure that will be used
to hold the list data. Besides the list element we have a header of the
list, containing several fields not relevant to the task of our reverse
function. We will use only the following fields from that header:
o RaiseError: Pointer to the error function.
o First: The first element of the list.
o count: The number of items in the list.
o Last: A pointer to the last element of the list.
o timestamp: A counter of the modifications done to the list.
The interface of the reverse function is simple: it receives a list to
reverse as input and returns an integer result code. A negative result
means failure (with different negative numbers as error codes) and a
positive number means success.
1 static int Reverse(List *l)
2 {
3 ListElement *Previous,*Current,*Next;
4
The first thing to do in a serious program is to check the validity of
the received arguments, i.e. test the preconditions as Bertrand Meyer
would say it. We test that the list handed to us is not Null (lines
5-8) and that the list is not read-only (lines 9-12) since we are going
to modify it.
If anything is wrong we return an error code after calling the error
function. Note that the error function is a field either in a global
structure
called iError (for interface Error) or is a field of the list itself. We
use the global interface iError in the case that the list is\Null, or the
list specific error function for the read only error. This is a powerful
feature of C called function pointers that we will discuss in more detail
later on.
5 if (l == NULL) {
6 iError.RaiseError("iList.Reverse",CONTAINER_ERROR_BADARG);
7 return CONTAINER_ERROR_BADARG;
8 }
9 if (l->Flags & CONTAINER_READONLY) {
10 l->RaiseError("iList.Reverse",CONTAINER_ERROR_READONLY);
11 return CONTAINER_ERROR_READONLY;
12 }
Then, we test for special conditions, i.e. for degenerate cases.
Obviously, reversing an empty list or a list with only one element is
very easy: we have nothing to do. We test for that (line 13) and return
success immediately if that is the case.
13 if (l->count < 2)
14 return 1;
Now, we setup our loop. We start with the first element (that must
exist since the list has more than one element, line 15). Since we are
going to reverse the list, the first element will be the last and the
last will be the first. We setup the last one immediately since we know
it in line 16. And before we start there is no previous element so we
set it to NULL.
15 Next = l->First;
16 l->Last = l->First;
17 Previous = NULL;
Now we go through the whole list. We save the "Next" value, and advance
it to the next element. Then, we set the value of the current element's
pointer to the previous, i.e. our current will point to previous
reversing the direction of the list.
18 while (Next) {
19 Current = Next;
20 Next = Next->Next;
21 Current->Next = Previous;
22 Previous = Current;
23 }
OK, we are done. We have gone through the whole list reversing
pointers, and we arrive at the cleanup. We should first establish our
"First" pointer, then we should ensure that our last element has
the Null marker, and that our list is marked as modified. We return a
positive number meaning success.
24 l->First = Previous;
25 l->Last->Next = NULL;
26 l->timestamp++;
27 return 1;
28 }
newcomers just ask a question or two, and do not see discussions
that try to explain code, teaching newcomers to the language how
a program works.
This is a second example (after hexdump.c) of commented code,
i.e. explaining step by step how a relatively simple operation
is done.
Reversing a linked list
-----------------------
This is an evergreen of data structures programming. In most classes
you will get an exercise like this:
Exercise 7: Given a list "L" reverse it without moving its contents.
The solution for this exercise is to go through the list, relinking the
"Next" pointers in the inverse order, without touching any data that
the list may hold. We will use the code of the C containers library and
study how it is done.
The library uses a simple structure "ListElement" whose definition runs
like this:
typedef struct _ListElement {
struct _ListElement *Next;
char Data[];
} ListElement;
We have a pointer to the next element of the list, and a chunk of data
of unspecified length. This is the basic structure that will be used
to hold the list data. Besides the list element we have a header of the
list, containing several fields not relevant to the task of our reverse
function. We will use only the following fields from that header:
o RaiseError: Pointer to the error function.
o First: The first element of the list.
o count: The number of items in the list.
o Last: A pointer to the last element of the list.
o timestamp: A counter of the modifications done to the list.
The interface of the reverse function is simple: it receives a list to
reverse as input and returns an integer result code. A negative result
means failure (with different negative numbers as error codes) and a
positive number means success.
1 static int Reverse(List *l)
2 {
3 ListElement *Previous,*Current,*Next;
4
The first thing to do in a serious program is to check the validity of
the received arguments, i.e. test the preconditions as Bertrand Meyer
would say it. We test that the list handed to us is not Null (lines
5-8) and that the list is not read-only (lines 9-12) since we are going
to modify it.
If anything is wrong we return an error code after calling the error
function. Note that the error function is a field either in a global
structure
called iError (for interface Error) or is a field of the list itself. We
use the global interface iError in the case that the list is\Null, or the
list specific error function for the read only error. This is a powerful
feature of C called function pointers that we will discuss in more detail
later on.
5 if (l == NULL) {
6 iError.RaiseError("iList.Reverse",CONTAINER_ERROR_BADARG);
7 return CONTAINER_ERROR_BADARG;
8 }
9 if (l->Flags & CONTAINER_READONLY) {
10 l->RaiseError("iList.Reverse",CONTAINER_ERROR_READONLY);
11 return CONTAINER_ERROR_READONLY;
12 }
Then, we test for special conditions, i.e. for degenerate cases.
Obviously, reversing an empty list or a list with only one element is
very easy: we have nothing to do. We test for that (line 13) and return
success immediately if that is the case.
13 if (l->count < 2)
14 return 1;
Now, we setup our loop. We start with the first element (that must
exist since the list has more than one element, line 15). Since we are
going to reverse the list, the first element will be the last and the
last will be the first. We setup the last one immediately since we know
it in line 16. And before we start there is no previous element so we
set it to NULL.
15 Next = l->First;
16 l->Last = l->First;
17 Previous = NULL;
Now we go through the whole list. We save the "Next" value, and advance
it to the next element. Then, we set the value of the current element's
pointer to the previous, i.e. our current will point to previous
reversing the direction of the list.
18 while (Next) {
19 Current = Next;
20 Next = Next->Next;
21 Current->Next = Previous;
22 Previous = Current;
23 }
OK, we are done. We have gone through the whole list reversing
pointers, and we arrive at the cleanup. We should first establish our
"First" pointer, then we should ensure that our last element has
the Null marker, and that our list is marked as modified. We return a
positive number meaning success.
24 l->First = Previous;
25 l->Last->Next = NULL;
26 l->timestamp++;
27 return 1;
28 }