LiteC and Lists

Part I

 

by 3DSki

3/2007

 

 

Introduction

 

With the advent of LiteC, the predicessor to CScript, a programmer can still experience the ease of a scripting language, yet have access to abilities that are closer to the capabilities of C/C++.  Many of the projects you work on may require minimal data needs and the use of the built-in data types available within the LiteC language.  However, other games you might want to produce under 3D Game Studio may be very data intensive and more complex data structures would be required, or at least more beneficial.  This is where LiteC steps in, and where CScript fades into the background.

 

This paper will focus on some of the more advanced capabilities offered by LiteC.  If you're a C veteran, some of the material that follows will look very familiar, or may serve as a reminder of some of classic C methodologies.  If you're new to LiteC, hold on to your seats, because you will come very close to implementing some C techniques once you've worked though the material with me!

 

To give us some focus, we will be studying list processing under LiteC... no, not arrays, but more advanced lists that are dynamic.  We will look at lists that can hold elements of any kind, lists that can grow, lists that can be torn down, and lists that can be split.  Most C programmers know this type of list as a "linked lists" and know that they're quire powerful.  The topic of linked lists is a very good one for demonstrating the more advanced LiteC features, over those offered by CScript.  In addition, it is often a topic of study for those learning C.  Furthermore, the principles learned from this study can be carried over to other uses.

 

So, now that some of you have "bowed out" and the rest of you can't wait to see what's in store, let's get on with it!

 

 

Lists

 

Most of you should already be familiar with arrays.  It should be no surprise that an array is a great list structure that offers fast access to data.  They can, and probably should be, used most frequently.  However, they do have a down side.  They are a little harder to work with if you don't know how many items you want the list to hold.   They are most often used to hold a predetermined number of items, like a list of names, of known vectors, or skills.  Conversely, if the data you want to store in an array is dynamic, then other techniques have to be use to keep track of how many items are in the list.  For example, let's say you want to create and manage a list of members in you're players group.  At times, you will be the only player in your group.  Later, players will join and leave the group, and maybe even move into another group.  In such a situation, you have to know the maximum number of entities that can be in any one of these groups at any time.  Each time you add a member, you have to keep track of the new count, in relationship to the preset max you set on the "group" arrays.

 

The above "brush over" of a basic list strategy using arrays. Linked lists open the horizon up to other alternatives to solve the problem of managing dynamically changing lists.  So, let's get on to describing what a linked list is.

 

 

Linked Lists

 

What is a linked list?  As you can tell from the name, this type of list has some kind of "link" involved.  What is a "link"?  A link is a "pointer" to another item in the list.  Let's look at a classic example that uses arrays.  Take a look at the following arrays:

 

       char* data = {'A','B','C','D','E','F','G','H','I','J'};

 

       int pointer = {0,1,2,3,4,5,6,7,8,9};

 

The above is, in fact, a linked list using arrays!  Why?  The array named "data" holds 10 characters, which are the data items we're interested in.  The other array named "pointer" contains our "links", which directly correspond to the positions of the items in the data array.  To be more forward about it, the item at index 0 of the "data" array is related to the "pointer" item at index 0.  The "pointer" array stores the index of the "data" array to visit.  Let's take our example to the next step so we see this in action:

 

data[pointer[0]] = 'A'

data[pointer[1]] = 'B'

data[pointer[2]] = 'C'

 

Above, you see that the value stored at an index in pointer is used as the index for our "data" array.  So, no biggy... why would you do that?  Well, he above example is very simplistic.  What if you had an array of values that was mixed up and you wanted to sort them, but wanted to keep the original data in the order you found it?  In this problem, we would determine the order, but only change the positional values stored in "pointer":

 

       char* data = {'H','B','J','D','E','F','G','A','I','C'};

 

       int pointer = {7,1,9,3,4,5,6,0,8,2};

 

Above, the values of "data" are unordered.  However, an algorithm was used to store the sorted indices of "data" into "pointer".  Now, we can visit each pointer item in order to return the items of "data" in order:

 

for(i = 0; i <=9;i++)

{

       print data[ pointer[i] ];

}

 

If you would have to play with the syntax a bit, but if you've gone though the LiteC tutorial you'll be able to past the above code into SED and give it a go.  You would have to add additional logic to write the characters to positions on the screen, to replace my example "print".

 

As a side note, classical examples of the above often name our "pointer" array, "next".  This makes a little more sense when reading the code ( "data[ next[0] ]" ), but I want you to get used to the idea of "pointer".

 

If you understand the concept I presented above, you are on you way to really knowing what a "linked list" is!

 

 

"Which Way Are You Going?"

 

There are actually different types of linked lists.  We're going to focus on the "single linked list", or what is also often called a "one-way linked list".  Once you have a good handle on what a "one-way linked list" is, you'll easily understand the basics of any other type of linked list.  Let's start by using an example that's similar to the one we used above. However, we need a special data type called a "struct", which is now available in the LiteC, but not present in CScript.

 

Programmers often think of a "struct" as being like a record, because it can hold all the items that make up a specific record.  For instance, if you wanted to create an ADDRESS record, you'd want it to contain items like, NAME, STREET, CITY, STATE and POSTAL CODE.  If your ADDRESS records where stored in a file, you would prefer to have a data type that could hold a single record's worth of data, making it easier to store and retrieve the records.  So, how do we create a "struct" data type?

 

Definition of a Struct:

 

struct character

{

   char letter[2];

   struct character* next;

}

 

Creation of a Struct:

 

struct character* A;

struct character* B;

struct character* C;

       …

 

Above, I’ve declared A, B and C “character” data items.  Notice that we have to used the key word “struct” in front of the declaration.  This gets to be a pain to type all the time, so most programmers use “typedef” command to clean-up the declarations:

 

typedef struct character

{

   char letter[2];

   struct character* next;

} CHARACTER;

 

The above “typedef” statement is used instead of our other version of the “struct” definition.  When using the typedef method of defining our structure, we can now use the following declarations instead:

 

CHARACTER* A;

CHARACTER* B;

CHARACTER* C;

       …

 

It is customary that the structure “type” name be in all uppercase letters; “CHARACTER”, in our case.  This helps distinguish it from other names in our code and serve as a reminder to everyone that the variable is a user defined type.

 

Now, we need to learn how to set and retrieve data from the struct’s members (properties).  In our example above we have declared an instance of A.  If we want to access the value its “next” member, we simple use the “.” (member operator):

 

A.next;       // … get A’s “next”

A.letter      // … get A’s “letter”

 

Now, we’ve not shown how to actually store data into our structure.  This is often easy, but we have one small detail to take care of in this case.  Our structure contains a “char []” (char array) and we cannot use a simple assignment to give it a value.  We must copy a literal string into the member, “letter”.  To do this, it’s easiest to gain access and use of C’s “strcpy” function:

 

char* strcpy(char* a, char* b);

int strcmp(char* a, char* b);           

 

strcpy = DefineApi("mscvrt!strcpy");

strcmp = DefineApi("mscvrt!strcmp");

 

Above, I show the commands necessary to use strcpy() from C’s standard library.  I’ve also included strcmp(), because we will need it later for comparing our values.  The first two lines are called function prototypes, which serve as a “blue print” of the function that’s defined elsewhere, but we want to make available for use.  The next two lines use LiteC to assign the C API functions to the names we want to use, which should correspond with the names of the prototypes, which I’ve set in bold.  The names of the prototypes actually become function pointers in LiteC, so the next statements assign the corresponding C functions to these.  Now, let’s assign some data to one of our CHARACTER* instances, A:

 

strcpy(A.letter, “A”);

A.next = NULL;

 

Above, we set all of A’s members, or properties.  You may wonder why we used the statement “A.next = NULL”.  The keyword, NULL, means “nothing”, and we can set pointers equal to this when we don’t know what we want them to point to at the time of initial assignment.  Remember, “next” is a CHARACTER*.

 

Not very difficult at all, is it?  Still, we have a problem.  Though each of our new "character" data type instances are related by name, they are just a hand full of data items in a program.  In our array example, the array was used to group the data items together.  A structure by itself is still only a building block, similar to a built in data type, like char or int.  We need a way to tie our CHARACTERs together, forming a list, just like in our array example.  How do we do this?

 

 

Hey, Hook Me Up

 

Above, I touched on the fact that “next” is a CHARACTER pointer.  This is, in fact, the piece of data that will tell us the next CHARACTER that is connect to the one we’re currently looking at.  Let’s hardwire a few connections

 

CHARACTER* A;

CHARACTER* B;

CHARACTER* C;

 

void main()

{

       A = malloc( sizeof(CHARACTER) );

       strcpy(A.letter, “A”);

       A.next = NULL;

 

       B = malloc( sizeof(CHARACTER) );

       strcpy(B.letter, “B”);

       B.next = NULL;

 

       C = malloc( sizeof(CHARACTER) );

       strcpy(C.letter, “C”);

       C.next = NULL;

 

       // CONNECT THE ITEMS…

       A.next = B;

       B.next = C;

 

       // FREE THE ITEMS…

       free( A );

       free( B );

       free( C );

}

 

 

Now, remember, our goal is to create a chain of existing data items.  Once we create all of them, we then know the values to store into each of the CHARACTER* item's "next" member.  This is what the last program statements do above. Now, "A.letter" will retrieve the value, and "A.next" will point to the container of our next letter, CHARACTER* B!

 

Okay, so now we've set all our values.  But, programming isn't much fun if you can't enjoy the opportunity to "loop through" the values, just as we did with the array and a "for" loop.  So, now how do we do the same thing with our new list?

 

 

"Round and Round it Goes, Where it Stops, We Surely Know"

 

Looping through the items in a linked list really isn't that difficult, but we need to add one more piece of information, and I need to make you aware of one other point.  The additional piece of code we need is one additional CHARACTER* that will serve as a temporary variable.  The other item you need to be aware of is, where the list ends!

 

If you remember, we initially set all our "next" members to NULL.  We then set "next" to each consecutive item to the next item we want in the list.  But, for the very last item, "C", we left "next" to equal NULL.  This all important NULL for the last 'next'" is key to programming our loop!  Here's the code:

 

CHARACTER* current;

       …

Void main()

{

       …

       current = A;

       while( current != NULL)

       {

              print current.letter;

              current = current.next;

       }

}

 

First, I created a temporary variable called "current".  This will hold each CHARACTER* as we get them out of our list.  Next, I set current to the first item of our list, A.  Now, we are ready to enter our "while" loop and can follow its workings.

 

When we start off, "current" equals A, so certainly isn't NULL, so we continue.  I use pseudo code to show that we're printing the value of the "letter" member of the current "character", A.  Now, at the bottom of the loop, we set "current" equal to "current.next", which is B.  Since "current" equals B, we go through the loop again.  Since "current" is "B", current is set to B.next, which is C.  C’s “next” is NULL, so current is NULL, and we’re kicked out of the loop. 

 

Though we will not go into detail of how to sort this new beast, assuming it was scrambled and we want the characters in alphabetical order.  However, I will touch on some key points of what would be needed and how sorting works with linked lists, in general.  In the above example, we knew our first item was A… no problem!  Well, when sorting, your algorithm will need a way to track and find the beginning of the list.  This value is usually placed in another temporary pointer variable called "start".  Then, after the sort is done, you could use the same looping technique as we used above, but set "current" equal to "start".   There are a number of sorting techniques and this topic can get complex.  But, the basic operation involves swapping the "next" values of each of the list items, taking special care of updating “start” and “end” during the sorting process.

 

 

"Our Ever Changing World"

 

Our world is always changing.  Sometimes we do have lists of items that were not meant to change, so they live happily in arrays and we are overjoyed to work with them as such.  But, what if we want to add and remove items from a list?  As stated earlier, you would not want to go through the trouble of working with a linked list unless it was a list that is meant to change.  It just doesn't make sense to design and create the structure, create each item separately, then hardwire all the connections between them!   Link lists are just another option in your programming arsenal.  It can be considered a more advanced technique and meant to be used with lists that must be managed in a more dynamic way, when compared to arrays.

 

In this section, we'll look at how a one-way linked list is actually created and give you a few more tools for your arsenal!  In our introductory example, I hard-coded the construction so it would be easy for you see the relationship between the elements.  Rather than hard-hard code each list element, I'll show you how to create each element and add it to a list in a more dynamic way.  Let's face it... you may very well be reading your data out of a file and need to grab the each record, store it in the list, and want to easily move on to reading the next item.  At any rate, it is one of those cases that will come up and you'll want to be able to easily handle it.

 

 

"The Alpha and the Omega"

 

A list always has a beginning and an end.  However, when we create our elements and add them to the list dynamically, the beginning and the end of the list isn't clear, so enters some temporary struct pointers to keep track of these... "start" and "end".  You can name them what you want, but "start" and "end" are simple statements to what they represent... the start of the list and the end of the list.  In addition, you'll need that "current" pointer we created to help keep track of the current item you're working with.  We'll still keep with our original example, but rebuild it using a new method.

 

LiteC didn’t seem to like me initializing my CHARACTER* values to NULL globally; outside of a function.  So, I’ve created a function to do the initializing of all my working CHARACTER* items, which should be called at the top of main().

 

CHARACTER* current;

CHARACTER* start;

CHARACTER* end;

       …

 

function initListVariables()

{

       current = NULL;

       start = NULL;

       end = NULL;

}

       …

void main()

{

       initListVariables();

       …

}

 

 

 

"The Power to Create"

 

Now, for we are ready to show you a trick that might be new to you... not so much a "trick", but some items that might be foreign to you... malloc() and sizeof(), with malloc() being the workhorse.

 

The function, malloc(), can be used in LiteC and used to dynamically request memory allocation; this function is also available in C.  In essence, it creates a variable for you on the fly!  However, before you get all excited, this ability comes with just as much responsibility.  If you created it, you must destroy it!  How is that done?  With a sledge hammer called, free().  So, before you go off trying to create everything under the sun, know that YOU must free up the memory before your application exits!  We'll show how this is done with a list... after we create one, of course. 

 

What's sizeof() used for?  Simply put, if you want to ask for memory, you need to know how much to ask for.  Well, that would be much fun if you had a complex structure full of different built-in data types... you have to determine each of their sizes and and them up by hand!  This is where sizeof() comes into play.  Here's an example that fits the work we've been doing:

 

current = malloc( sizeof( character ) );

 

After this statement, you have a newly created "character" item, pointed to by "current"!  Next, you have to put some data into it and set up our other data items related to the list:

 

strcpy(current.letter, “A”);

current.next = NULL;

start = current;

end = current;

 

The three most important things to note here is, you need to set "next" to NULL, since we don't know the real value we want to give it yet, and set "start" and "end" to the address of this first element; since "current" is the only element, the "start" and "end" of the list are the same, and equals "current".  And, by the way, you now have a 1 item list!

 

Now, just to stress the point, and to make the code runnable "as is" add the following to the bottom of main():

 

free(current);

 

Always remember to "free" what you ask to be allocated.  What about "start" and "end"?  Well, you didn't actually ask for anything to be allocated to these, directly.  They only happen to share the address of "current" which you did request allocation for.  In addition, you could have easily coded, free(start) or free(end), because they all do hold he address of the location of the allocated memory, since we only have one link; (start == end).  So, free() works with the address of the location, not the location itself, to free the allocated memory.

 

 

Time to Build

 

So we have our single item list.  We need to build something that will do the adding for us... but this sounds harder than it really is.  Basically, we need a function that takes in a new item and connects it to "end".  To do this, we'll rearrange some of our lines of code:

 

void add(CHARACTER* link)

{

       link.next = NULL;

       if (start == null )

       {

              start = link;

       } else {

              end.next = link;

       }

       end = link;

}

            …

current = malloc( sizeof( CHARACTER ) );

strcpy(current.letter, “A”);

add( current );

 

I moved the setting of the created link’s "next" pointer to NULL to our new add() method; this goes outside of the "if-else" since this always needs to be done.  I also placed the logic for setting  "start" and "end" inside our add() function to let it keep track of these for us.  Remember, "start" and "end" are global, so accessible from within the function without having to pass them in.

 

Now, here comes the hardest part, because it is abstract, or not quite what it seems.  The "end" item is equal to the address of the item that was previously determined to be the end of the list.  The first time the method is called "start" and "end" are set equal to the single item.  The next time the method is called with a new "link", you can think of "end" being equal to "start" and we are actually setting "start.next" to our new "link", then setting "end" to point to the new last item of our list, "link".  Such is the same with all subsequent calls; "end" is the previous end, whose "next" we set to our new link, then “end” is reset to this new link.

 

 

Clean-up Time

 

Cleaning our list up with free() is not as hard as you might think, and another new function would be best for this.  Remember earlier when we looped through each item of the list until we reached the end.  Well, that is exactly what we need to do to call free() on each of are created items, but with a little bit of juggling added:

 

void freeList()

{

       current = start;

 

       while( current != NULL)

       {

              start = current.next;

              free(current)

              current = start;

       }

}

 

What the heck?  What's going on here, and why?  Well, we cannot just free any old item.  You need to make sure you get the "current's next" before you free the current!  This is an easy mistake to make when you first start working with linked lists.  In the code above, "current" is first set to "start", so current is ready to be freed.  But, before we free current, we must move its "next" over into "start" for save keeping, then ask the OS to free "current".  Finally, "start" (the next item's address) is moved back into current, where we can check to see if it’s NULL.  This continues until "current" is NULL;

 

 

Deleting is Not the Same as Freeing

 

We learned how to make our list grow, walk our way through it, and even free all the memory its items were taking up.  However, removing an item is not the same as simply freeing an item, so don’t fall into this trap!   We depend on the “next” of one item to take us to the next.  If you find and simply free an item, thinking your list can still be used, you will sadly be mistaken.  If you only free() an item, the “next” pointer will still point to what you just removed from memory and the rest of your list lives in darkness forever… until your application exits and causes a memory fault!  To remove an item, you must do a little bit of a juggling act, just as we did when we freed the list, taking special care of each item’s “next” pointer, since it is our only compass for getting around.  To do this, we need one more temporary pointer variable we’ll call “previous”:

 

CHARACTER* previous;

 

Let’s look at why we need “previous”.  Let’s say we have three items in our list, A, B and C, and we want to delete B.  To do this, we start going through our items to see if “current” equals B.  But, before we free B, we must first tell A’s “next” to point to C to keep the items connected.  But, if we are already at B, how can we tell what A is?  Once the focus is on B, all we know is B, and whatever “next” points to, and nothing about what comes before.  This is where “previous” becomes the “king pin”. 

 

The other thing we need to be able to do is locate, or find, the item in the list we want to delete.  In our case, we need a function that looks for the item that contains a letter (‘B’ in our example) and return it’s address.  If this function doesn’t find a match, it should return NULL.  The following is a full working code listing that demonstrates what we’ve done earlier, as well as some of the additional features we will need:

 

LINKEDLIST.C LISTING

 

#include <acknex.h>

#include <default.c>

 

#define TRUE        1

#define FALSE        0

 

char* strcpy(char* a, char* b);

int strcmp(char* a, char* b);           

strcpy = DefineApi("mscvrt!strcpy");

strcmp = DefineApi("mscvrt!strcmp");

 

typedef struct character

{

   char letter[2];

   struct character* next;

} CHARACTER; 

 

TEXT* link_txt =    

{

  layer = 1;

  pos_x = 10;

  pos_y = 10;

  size_y = 60;

  offset_y = 0;

  flags = VISIBLE;

}

 

CHARACTER* start;

CHARACTER* end;

CHARACTER* current;

CHARACTER* previous;

 

void initListVariables()

{

       start = NULL;

       end = NULL;

       current = NULL;

       previous = NULL;

}

 

void add( CHARACTER* link)

{

       link.next = NULL;  // "next" is always NULL for a new link

       if ( start == NULL )  // if the list is empty...

       {

              start = link;

       } else {

              end.next = link;

       }

       end = link;

}

 

void freeList()

{

       current = start;

 

       while( current != NULL )

       {

              start = current.next;

              free(current);

              current = start;

       }

       initListVariables();

}

 

CHARACTER* find( char* value)

{

       current = start;

 

       while( current != NULL )

       {

              if ( strcmp( current.letter,value) == 0)

                     return current;

              current = current.next;

       }

       return NULL;

}

 

CHARACTER* findPrevious( CHARACTER* item)

{

       if ( item != NULL )  // real item?

       {

              current = start;

              while( current != NULL )

              {

                     if ( current.next == item )

                           return current;

                     current = current.next;

              }

              return NULL;

       } else {

              return NULL;

       }

}

 

int delete( CHARACTER* item )

{

       if ( item != NULL && start != NULL && end != NULL ) 

       // if real item and the list isn't empty…

       {

              if ( item == end)    // deleting the end item?

              {

                     if ( end == start ) // last item, so just free the list

                     {

                           freeList();

                           return TRUE;

                     } else {  // ...more than 1 item and deleting the end...

                           previous = findPrevious( item );

                           if ( previous )

                           {

                                  previous.next = NULL;

                                  end = previous;

                                  free(item);

                                  return TRUE;

                           }

                     }

              } else {

                     if ( item == start ) // deleting the first list item?

                     {

                           start = item.next;

                           free(item);

                           return TRUE;

                     } else {             // deleting an item in between?

                           previous = findPrevious( item );

                           previous.next = item.next;

                           free(item);

                           return TRUE;

                     }

              }

       }

       return FALSE;

}

 

void main()

{

       int deleted;

 

       initListVariables();

 

       current = malloc( sizeof( CHARACTER ) );  // A

       strcpy(current.letter, "A" );

       if ( current ) add( current );

      

       current = malloc( sizeof( CHARACTER ) );  // B

       strcpy(current.letter, "B" );

       if ( current ) add( current );

      

       current = malloc( sizeof( CHARACTER ) );  // C

       strcpy(current.letter, "C" );

       if ( current ) add( current );

 

       // Find link that holds “B” and display its “letter”, then delete it…

       current = find("B");

       if ( current )

       {

              // Display "B"

              // Use LiteC's str_cpy() to copy char* to STRING*,

              // TEXT* link_txt for display...

              str_cpy((link_txt.pstring)[0],current.letter);

             

              deleted = delete( current );   // request to delete 'B'

       }

      

       // TEST: Attempt to find “B” – If not found, display “No B”…

       current = find("B");

       if ( current == NULL )

       {

              // Display "No B"

              str_cpy((link_txt.pstring)[0], "No B");

       }

       freeList();

}

 

 

A good portion of the code above should look familiar to you.  However, I’ve added a TEXT* so we can display the contents of our links.  We use LiteC’s str_cpy() to copy the char contents of a link into a STRING*, a member of TEXT*.  Don’t confuse this with C’s strcpy() function!

 

The three functions you need to really study are find(), findPrevious() and delete().  Let’s look at find() and findPrevious(), then we’ll move on to delete().

 

Notice that this version of find() takes a char* value.  This means you will pass it a string value, such as “B” or “red”, or any text between quotes.  If each of your links contained a different item to find, you would obviously be interested in finding different data, perhaps a number, so would have to create your own version of find().  However, your re-write would only involve the comparison between the value you’re looking for and that held by the link member.  Other than that, the basic logic would remain unchanged.  Let’s step through how find() works.

 

 

CHARACTER* find( char* value)

{

       current = start;

 

       while( current != NULL )

       {

              if ( strcmp( current.letter,value) == 0)

                     return current;

              current = current.next;

       }

       return NULL;

}

 

First, “current” is set to “start” so we can begin the search for the link at the beginning of the linked list, then we begin our familiar loop, which will end at the end of the list, or if the link we are looking for is found.  In the loop we compare the current link’s “letter” to the value we want to find, “value”.  The strcmp() C function will return 0  if the two are equal; less than 0 if current.letter < value, greater than 0 if current.letter > value… which should give you a clue of how you could do comparisons for a sort routine.  So, if they are equal, we return the current link.  If the link is not found, the function will return NULL.  In main(), you will notice that we check the return value to determine if the item was found, right after “current = find(“B”)”.

 

The findPrevious() function is a little different.  In this method, we are not concerned about any of the data that might be contained by a link.  We are passing it an actual CHARACTER*, to find the CHARACTER* that precedes it in the list.  If you pass findPrevious() an empty link, or it can’t be found, it will return NULL.  If you pass in a valid link, we once gain loop through the link, starting at the beginning of the list.  However, we are looking to see if “current.next” is equal to the CHARACTER* we pass in.  If “current.next” does equal this value, then “current” is the link that precedes it, so we return it.

 

Next, we have our delete() function.  It is a little more involve and contains a lot more code that will be foreign to you.  This will require more discussion.  First, let’s talk about the general requirements for any delete() function.  When coding you should place some thought toward all the special cases that might exist in what you’re working with.  In the case of our list, and the item you want to remove, there are a number of special cases.  First, the item passed in may be invalid (NULL) or the list could be empty.  These are extreme cases, but they can happen, so you need to account for them.  We also might not find the link we want to delete.  In all of these cases, we want the function to return FALSE (defined as 0), otherwise we’ll return TRUE (defined as 1).  Returning either of these values allows us to confirm whether the value was deleted.

 

We have four other special cases, in the event that the link exists and can be deleted from the list.  The link could be the “start” link, the “end” link or some other link in between, or it could be the last item in the list.  Each of these conditions must be handled differently.

 

If the link we are deleting is the last item in the list (if item equals “end” and “end” equals start), then we can simply call our freeList() method to handle all the clean up for us.

 

If the link we are deleting is the “end” link, then we need to find the item that precedes it, make it the new “end”, make it’s “next” equal to NULL (since it will no longer have a item next to it) then free() the link we want to remove.

 

If the link we are deleting is the “start” link, we need to make “start” equal to it’s “next”, then delete the link using free();

 

If the link is someplace in between “start” and “end”, we have to form a new link between the item just prior to the one we are deleting and the one after it (previous.next needs to be set equal to the “next” of the item we are deleting).  After creating a new connection between the first half of the list and the second, we can then free() the item we want to remove.

 

So, through determining the special cases of this deletion problem, we were able to break up the problem into manageable pieces and solve each case.  Though delete() looks complex, it is broken up by “if” statements that test for the special cases we’ve discussed.  However, it is not uncommon that you don’t begin to understand that special cases exist until you run into problems with the code you’ve written.  For instance, you might have deleted the first item in the list, but forgotten to reset “start” to the new list beginning.  In this case you may very well know that you still have 9 items in the list, but you get an error once you try to access the list, because the old “start” will no longer be valid.  You will just have to work through such problems in your own coding.  Still, this points out the importance of placing all the thought you can into determining if, and what, special cases in the problem you’re trying to solve.

 

 

Conclusion

 

I was actually planning on covering more topics in this tutorial, but we covered quite a bit of material for you to digest!  This will just motivate me to create more installments on the topic of linked lists, or list handling in general.  There are numerous other things you might like to do with the list we’ve worked with in this tutorial. 

 

For instance, you might want to delete an item, based on it’s position in the list, as opposed to its value.  In this tutorial, we found the item, then used it to call delete.  You might want to write an alternative delete() that takes a value as the parameter and looks for the item with this value for deletion.  Also, we assumed that we only wanted to delete a single item.  What if you wanted to delete all items that contained a certain value?   You now have a good bit of code to work from to try your hand at solving some of the list problems that might apply to the processing you want to do, according to your data needs.  A more straightforward addition to your study of the link list would be to actual create a data file and write the code that would read the data in via a loop, where each list item would be created, populated with the data you read in, then added to the list.

 

You might also want to experiment with how you might split one list into two separate ones.  List splitting would require a good amount of thought, with the focus on some of the special cases we’ve spoken of in our discussion of delete().  In fact, this might be a second installment, as a sequel to this tutorial!

 

As a final word, there are some special considerations for the code presented if you plan to eventually move on to 3D Game Studio SDK programming with C.  Mainly, we used the member operator ( “.” )  in all of our code to access members of our CHARACTER*.  Though LiteC allows for this, it is more portable if you use “->”; (item->letter), since this syntax is allowable under both languages.  In the case of C, however, you would be required to use “->”, because we are working with structure pointers, rather than a plain structure.  So, if you have ambitions to eventually move on to C, you might want to change the list-specific code to use the “->” operator, instead of “.”.

 

Other material can probably be found in books and the web, but I hope this tutorial was helpful!  Books aren’t cheap and not much would be out there for LiteC, which is specific to 3D Game Studio, for its 3D game and presentation programming.