CS 2213 Advanced Programming
Structures


Read Chapter 6 of the text.
Start of Class Thursday, Oct. 26, 2000
Start of Class Tuesday, Oct. 31, 2000
Start of Class Thursday, Nov. 2, 2000
Start of Class Tuesday, November 7, 2000
Start of Class Thursday, November 9, 2000
Start of Class Tuesday, November 14, 2000
Start of Class Thursday, November 16, 2000
Start of Class Tuesday, November 28, 2000
Previous Topic: Arrays and Pointers
Next Topic: Input and Output

Introduction

A structure is a collection of one or more variables, grouped together under a single name. It is like a class without any code.


6.1 Basics of Structures

One of the simplest classes in Java is the Point class which contains two integer fields x and y.

Here is how we would make a point structure in C:

   struct point {
      int x;
      int y;
   };
The identifier following the key word struct is called the structure tag and can be used to as a shorthand to represent the part of the structure definition which is in the following braces.

The variables named in the structure are called its members.
These names only have to be unique with the structure.
The same names can be used outside the structure to represent different variables.
The structure tag may also be the same as the name of a variable, but this should be avoided.

The struct definition defines a new type and variables can be declared with that type once the structure is declared.

struct point pt1, pt2, pt3;
defines three variables of type point.

You can initialize variables by listing the values of the members in braces:

struct point pt1 = {100,200};

You can reference the members of a structure with the same notations as you use for fields of a class:

struct point pt1, pt2, pt3;

pt1.x = 4;
pt1.y = 5;
printf("The point has x value %d and y value %d\n",pt1.x,pt1.y);
Structure definitions can be nested:
struct line {
   struct point pt1;
   struct point pt2;
};
struct line myline;
myline.pt1.x = 3;
myline.pt1.y = 4;
myline.pt2.x = 5;
myline.pt2.y = 7;
printf("My line starts at (%d,%d) and ends at (%d,%d)\n",
       myline.pt1.x,myline.pt1.y,myline.pt2.x,myline.pt2.y);


6.2 Structures and Functions

Consider the following program and its output:
#include <stdio.h>
 
struct point {
   int x;
   int y;
};

struct line {
   struct point pt1;
   struct point pt2;
};
 
void showpoint(char *msg, struct point apoint) {
 
   printf("%s is at (%d,%d)\n",msg,apoint.x,apoint.y);
 
}
 
void showline(char *msg, struct line aline) {
 
   printf("%s starts at (%d,%d) and ends at (%d,%d)\n\n",msg,
           aline.pt1.x, aline.pt1.y, aline.pt2.x, aline.pt2.y);
 
}
 
void changepoint(struct point apoint) {
 
   showpoint("In change point before change point is ",apoint);
   apoint.x++;
   showpoint("In change point after  change point is ",apoint);
 
}
 
struct point addpoints(struct point point1, struct point point2) {
 
   point1.x = point1.x + point2.x;
   point1.y = point1.y + point2.y;
 
   return point1;
 
}
 
int main() {
 
   struct line myline;
 
   struct point firstpoint = {4, 12};
   struct point secondpoint;
 
   secondpoint = firstpoint;
   secondpoint.x++;
   secondpoint.y--;
 
   myline.pt1 = firstpoint;
   myline.pt2 = secondpoint;
 
   showpoint("firstpoint",firstpoint);
   showpoint("secondpoint",secondpoint);
   showline("My line ",myline);
 
   firstpoint.x = 17;
   showpoint("firstpoint",firstpoint);
   showpoint("secondpoint",secondpoint);
   showline("My line ",myline);
 
   changepoint(firstpoint);
   showpoint("After changepoint returns,    point is ",firstpoint);
 
   printf("\n");
   showpoint("Before addpoints,  firstpoint",firstpoint);
   showpoint("Before addpoints, secondpoint",secondpoint);
   secondpoint = addpoints(firstpoint,secondpoint);
   showpoint("After  addpoints,  firstpoint",firstpoint);
   showpoint("After  addpoints, secondpoint",secondpoint);
 
   return 0;
 
}
Here is the output generated:
firstpoint is at (4,12)
secondpoint is at (5,11)
My line  starts at (4,12) and ends at (5,11)

firstpoint is at (17,12)
secondpoint is at (5,11)
My line  starts at (4,12) and ends at (5,11)

In change point before change point is  is at (17,12)
In change point after  change point is  is at (18,12)
After changepoint returns,    point is  is at (17,12)

Before addpoints,  firstpoint is at (17,12)
Before addpoints, secondpoint is at (5,11)
After  addpoints,  firstpoint is at (17,12)
After  addpoints, secondpoint is at (22,23)

Note that in C, function parameters are always passed by value.

Why does it work to pass an array to a function that changes the array, but it does not work to pass and int or struct point to a function to change its value?

Differences between Java and C

Java Code:                  C Code:

P1 = new Point(1,2);        struct point p1 = {1,2};
P2 = new Point(3,4);        struct point p2 = {3,4};
P2 = P1;                    p2 = p1;
P1.x = 17;                  p1.x = 17;
What are the points now? Why?

We can simulate the new Point of Java with the following:

struct point makepoint(int x, int y) {
   struct point temp;
   temp.x = x;
   temp.y = y;
   return temp;
}

How do you pass a structure to a function so that it can be modified?
You do this the same way as you would with an int.
You pass a pointer to it.

struct point *mypointp;
defines a pointer to struct point.
You can access the members as follows:
struct point *mypointp;
struct point p;
mypointp = &p;
(*mypointp).x = 17;
Note that the parens are needed because . has a higher precedence than *.

Because this combination occurs so often, there is an operator to handle this directly:
mypointp -> x is the same as (*mypointp).x, so we can write:

mypointp->x = 17;
Here is a new version of changepoint
#include <stdio.h>

struct point {
   int x;
   int y;
};

void showpoint(char *msg, struct point apoint) {

   printf("%s is at (%d,%d)\n",msg,apoint.x,apoint.y);

}

void changepointok(struct point *apointp) {

   showpoint("In change point before change point is ",*apointp);
   apointp -> x++;
   showpoint("In change point after  change point is ",*apointp);

}

int main() {

   struct point firstpoint = {4, 12};

   changepointok(&firstpoint);
   showpoint("After changepoint returns,    point is ",firstpoint);

   return 0;

}
Which produces output:
In change point before change point is  is at (4,12)
In change point after  change point is  is at (5,12)
After changepoint returns,    point is  is at (5,12)

What is wrong with the following?:

struct point *mypointp;
struct point p;
*mypointp = p;
(*mypointp).x = 17;
Note that the operators with highest precedence are ., ->, () for function calls, and [] for array subscripts.


Here is some information about Programming Assignment 2.

Consider the following:

struct stringwithlength {
   int len;
   char *str;
} *p;
This defines a variable, p which is a pointer to this structure.
++p->len increments len because -> has a high precedence.
(p++)->len increments p after to gets the len member.

The sizeof operator can tell you the size of a variable or a type.
You can use either:
sizeof object
or
sizeof(typename)

For example, on our system
sizeof(int)
will return 4 and
sizeof(int *)
will also return 4.

What about sizeof(stringwithlength)?

This is an error, but
What about sizeof(struct stringwithlength)?

This gives 8.

Suppose we had

struct newstringwithlength { 
   int len; 
   char str[10]; 
}; 
What is sizeof(struct newstringwithlength)?

16    Why is that?

consider

   struct newstringwithlength x;
   printf("x.len has size %d\n",sizeof(x.len));
   printf("x.str has size %d\n",sizeof(x.str));
   printf("x has size %d\n",sizeof(x));
This gives
x.len has size 4
x.str has size 10
x has size 16
Conclusion: The whole is sometimes bigger than the sum of its parts.

Results may be different on different hardware, even when ints are 4 bytes.


6.7 Typedef

Note that we are covering this out of order.

The library routine strlen has the prototype:

      size_t strlen(const char *s);
This returns the length of the string.
What is size_t?
This is just a made up type representing an unsigned int and is defined in stdio.h as
typedef unsigned int    size_t;
typedef is used to give new names to existing types.
The format of a type definition is
typedef oldname newname

Typedefs are often used with structures. For example

typedef struct stringval {
   char *str;
   int val;
} strval;
Would allow us to make a declaration like:
   strval sval;
and sval would then be a struct stringval.

One usual way of doing this is to use the same name for the new type as the structure name:

typedef struct stringval { 
   char *str; 
   int val; 
} stringval; 
and then the following would be equivalent:
   struct stringval sval;
   stringval sval;
You can even leave out the structure name completely:
typedef struct { 
   char *str; 
   int val; 
} stringval; 
and we could still say:
   stringval sval;
but struct stringval sval would now be undefined.

What is the difference between the following:

typedef struct {              struct {
   char *str;                    char *str;
   int val;                      int val;
} stringval;                  } stringval;

The one on the left defines stringval as a new type.
The one on the right defines a variable, stringval, which can contain a structure.


6.3 Arrays of Structures

Consider the structure from out Programming Assignment 2 (now with a typedef):
typedef struct {
   char *str;
   int val;
} stringval;
Instead of using a hash table, assume that we have just an array of these:
#define NUMSTRINGS 1000
stringval mystrings[NUMSTRINGS];
mystrings is and array of structures.
Let us assume that we have initialized this array and sorted it.
We will now write the lookup part:
int binsearch(char *str, stringval *sval, int size);
Where str is the string to search for and size is the size of the array.
We will return the index if the string is found and -1 if not found.
int binsearch(char *str, stringval mystrings[], int size) {
   int cond;
   int low;
   int high;
   int mid;

   low = 0;
   high = size - 1;
   while (low <= high) {
      mid = (low + high)/2;
      cond = strcmp(str,mystrings[mid].str);
      if (cond < 0)
         high = mid - 1;
      else if (cond > 0)
         low = mid + 1;
      else
         return mid;
    }
    return -1;
}
The idea is that the string we are looking for, if it exists, is between mystrings[low] and mystrings[high], inclusive.


6.4 Pointers to Structures

We can look at the same binary search problem in terms of pointers, rather than arrays.
Instead of using integers low, mid, high we can use pointers instead.
Our new binsearch will look like this:
stringval *binsearch(char *str, stringval *mystrings, int size);
Now we return a pointer to the found entry, or NULL if it is not found.
Here is the obvious translation of the above function:
stringval *binsearch(char *str, stringval *mystrings, int size) {
   int cond;
   stringval *low;
   stringval *high;
   stringval *mid;

   low = mystrings;
   high = &mystrings[size-1];
   while (low <= high) {
      mid = low + (high-low)/2;
      cond = strcmp(str,mid->str);
      if (cond < 0)
         high = mid - 1;
      else if (cond > 0)
         low = mid + 1;
      else
         return mid;
   }
   return NULL;
}
What is wrong with this?

The problem is that we are not allowed to dereference pointers outside the array, and in fact, we should not even be allowed to computer a pointer to something outside of the array.

Suppose that the array has 2 elements starting at location 1000 and that sizeof(stringval) is 8.

Suppose also that the string we are looking for is not in the array and in fact is smaller than any of the stored strings.

We start with (addresses in bold):
low = 1000
high = 1008
mid = 1000 + (1008 - 1000)/2 = 1000 + 1/2 = 1000 + 0 = 1000;
cond < 0 so
high = 1000 - 1 = 992;
But we are not even allowed to make such a pointer this way.

We can solve the problem by reorganizing the code slightly.
The C standard allows us to compute (but not dereference) the pointer just past the end of an array.

In the following, the string we are looking for is between mystrings[low] and mystrings[high], including the left endpoint, but not including the right endpoint.

 
stringval *binsearch(char *str, stringval *mystrings, int size) {
   int cond; 
   stringval *low; 
   stringval *high; 
   stringval *mid; 
 
   low = mystrings; 
   high = &mystrings[size]; 
   while (low < high) { 
      mid = low + (high-low)/2; 
      cond = strcmp(str,mid->str); 
      if (cond < 0)
         high = mid;
      else if (cond > 0)
         low = mid + 1;
      else
         return mid;
   }
   return NULL;
}


6.5 Self-referential Structures

The example we will look at here involves linked lists.

We have already looked at an array implementation of a stack.

This has the limitation of a fixed maximum size.

We will consider a stack of stringval items.
Each stack element will contain a stringval and a pointer to the next element on the stack.

struct stackentry {
   stringval sval;
   struct stackentry *next;
};
A structure is not allowed to contain a copy of itself, but it can contain a pointer to itself.
It would be useful to make a typedef for this:
typedef struct stackentry {
   stringval sval;
   struct stackentry *next;
} stackentry;
so that we can refer to it by the name stackentry rather than struct stackentry.

Notice that the following is not allowed:

typedef struct stackentry {
   stringval sval;
   stackentry *next;
} stackentry;
because the name stackentry (without the struct) is not defined until the closing brace.

We will make a "stack object" which will use a linked list to implement the stack.
Here is the contents of the stack.h file:

typedef struct stringval {
   char *str;
   int val;
} stringval;

int push(stringval sval);
stringval *pop(void);
void printstack(void);

The push returns true on success and false on failure.
The pop removes the item from the top of the stack and returns a pointer to it.
It returns NULL if the stack was empty.

There are many ways to represent a stack using linked lists.
In our implementation, an entry on the stack will contain a stringval and a pointer to the next element on the stack.

The stack itself will just be a pointer to a stack entry.
The pointer will be NULL when the stack is empty.

Here is what the stack.c file might look like:

#include <stdio.h>
#include <stdlib.h>
#include "stack.h"

typedef struct stackentry {
   stringval sval;
   struct stackentry *next;
} stackentry;

typedef stackentry *stack;

static stack mystack = NULL;

static stackentry *make_entry(stringval sval) {
   ...
}

/* return true if success, false if error */
int push(stringval sval) {
   ...
}

/* remove and return pointer to top item if OK, NULL if error */
stringval *pop() {
   stringval *svalp;
   ...
   return svalp;
}

void printstack() {
   stackentry *this;
   int count = 0;
   this = mystack;
   printf("Stack follows:\n");
   while (this != NULL) {
      printf("%10d %s\n",this->sval.val,this->sval.str);
      this = this->next;
      count++;
   }
   printf("Number of entries found: %d\n",count);
}

Here is a main program that can be used to test the stack:

#include <stdio.h>
#include <stdlib.h>
#include "stack.h"

static void testpush(char *str, int val) {
   stringval sval;
   sval.val = val;
   sval.str = str;
   printf("Before push\n");
   printstack();
   if (push(sval) < 0)
      printf("push failed\n");
   printf("After push of %s\n",str);
   printstack();
   printf("\n");
}

static void testpop() {
   stringval *svalp;
   printf("Before pop\n");
   printstack();
   svalp = pop();
   if (svalp == NULL)
      printf("pop failed\n");
   else
     printf("poped: %10d %s\n",svalp->val,svalp->str);
   printf("After pop\n");
   printstack();
   printf("\n");
}

int main() {
   testpop();
   testpush("this is pushed first",10);
   testpush("this is pushed second",20);
   testpop();
   testpop();
   testpop();
   testpush("this is pushed first again",30);
   testpush("this is pushed second again",40);
   testpop();
   return 0;
}
Here is the output generated by this:
Before pop
Stack follows:
Number of entries found: 0
pop failed
After pop
Stack follows:
Number of entries found: 0

Before push
Stack follows:
Number of entries found: 0
After push of this is pushed first
Stack follows:
        10 this is pushed first
Number of entries found: 1

Before push
Stack follows:
        10 this is pushed first
Number of entries found: 1
After push of this is pushed second
Stack follows:
        10 this is pushed first
        20 this is pushed second
Number of entries found: 2

Before pop
Stack follows:
        10 this is pushed first
        20 this is pushed second
Number of entries found: 2
poped:         10 this is pushed first
After pop
Stack follows:
        20 this is pushed second
Number of entries found: 1

Before pop
Stack follows:
        20 this is pushed second
Number of entries found: 1
poped:         20 this is pushed second
After pop
Stack follows:
Number of entries found: 0

Before pop
Stack follows:
Number of entries found: 0
pop failed
After pop
Stack follows:
Number of entries found: 0

Before push
Stack follows:
Number of entries found: 0
After push of this is pushed first again
Stack follows:
        30 this is pushed first again
Number of entries found: 1

Before push
Stack follows:
        30 this is pushed first again
Number of entries found: 1
After push of this is pushed second again
Stack follows:
        30 this is pushed first again
        40 this is pushed second again
Number of entries found: 2

Before pop
Stack follows:
        30 this is pushed first again
        40 this is pushed second again
Number of entries found: 2
poped:         30 this is pushed first again
After pop
Stack follows:
        40 this is pushed second again
Number of entries found: 1
Look at this output carefully!
Notice that: Here is what the output should look like:
Before pop
Stack follows:
Number of entries found: 0
pop failed
After pop
Stack follows:
Number of entries found: 0

Before push
Stack follows:
Number of entries found: 0
After push of this is pushed first
Stack follows:
        10 this is pushed first
Number of entries found: 1

Before push
Stack follows:
        10 this is pushed first
Number of entries found: 1
After push of this is pushed second
Stack follows:
        20 this is pushed second
        10 this is pushed first
Number of entries found: 2

Before pop
Stack follows:
        20 this is pushed second
        10 this is pushed first
Number of entries found: 2
poped:         20 this is pushed second
After pop
Stack follows:
        10 this is pushed first
Number of entries found: 1

Before pop
Stack follows:
        10 this is pushed first
Number of entries found: 1
poped:         10 this is pushed first
After pop
Stack follows:
Number of entries found: 0

Before pop
Stack follows:
Number of entries found: 0
pop failed
After pop
Stack follows:
Number of entries found: 0

Before push
Stack follows:
Number of entries found: 0
After push of this is pushed first again
Stack follows:
        30 this is pushed first again
Number of entries found: 1

Before push
Stack follows:
        30 this is pushed first again
Number of entries found: 1
After push of this is pushed second again
Stack follows:
        40 this is pushed second again
        30 this is pushed first again
Number of entries found: 2

Before pop
Stack follows:
        40 this is pushed second again
        30 this is pushed first again
Number of entries found: 2
poped:         40 this is pushed second again
After pop
Stack follows:
        30 this is pushed first again
Number of entries found: 1

Time to write int push(stringval sval) and stringval *pop() as well as static stackentry *make_entry(stringval sval)

Here is the code we wrote in class on Tuesday:

static stackentry *make_entry(stringval sval) {
   stackentry *entryp;
   entryp =(stackentry *)malloc(sizeof(stackentry));
   if(entryp==NULL)
      return NULL;
   entryp->sval =sval;
   entryp->next =NULL;
   return entryp;
}

int push(stringval sval){
    stackentry *entryp;
    
    entryp = make_entry(sval);
    if(entryp == NULL)
       return 0;
    entryp-->next = mystack;
    mystack = entryp;
    return 1;
}

stringval *pop(){
   stringval *sval;
   if(mystack == NULL)
     return NULL;
   ...





In the pop operation, be careful about what you free and its relationship to what it returned.

An alternative is to define another stack function:

int stackempty(void);
which returns true if the stack is empty and false otherwise.
We can then define the pop operation as:
stringval pop(void);
which is only allowed to be called when the stack is not empty.

Here is what we wrote in class:

stringval *pop() { 
    stringval *svalp; 
    if (mystack == NULL) 
        return NULL;   
    svalp = &mystack -> next;
    return svalp;
} 
We discussed the fact that this returned a pointer so that we could determine whether the pop failed.
The problem with his is that in this version it is not possible to free the node.
Even if we created a stringval and returned a pointer to this, the calling program would have to free it.

An alternative is to return a stringval directly, but have another function to test the stack for empty. We looked at these two implementations:

int stackempty() {
    if(mystack == NULL)
        return 1;
    else
        return O;
}    
 
int stackempty() {
    return (mystack == NULL);
}
We then wrote the alternative pop:
stringval pop() {
    stringval sval;
    stackentry *entryp;
    sval = mystack -> sval;
    entryp = mystack;
    mystack = mystack -> next;
    free (entryp);
    return sval;
}


Before returning to queues, here is some information about Programming Assignment 3.

And here is some information about the exam next week.


A queue implementation

You might want to review the notes on stacks which have been updated to reflect what we did on class.

Review: with a few changes for clarification, e.g. back to prev.

An example of a doubly linked list.

In a queue, items are inserted at one end and taken out of the other.

A node entry might look like this:

typedef struct nodeentry {
   stringval sval;
   struct nodeentry *next;
   struct nodeentry *prev;
} nodeentry;
with a pointer to both the next and the previous entry.
In either case, a NULL pointer is used to indicate an end of the list.

The queue itself will need a pointer to the start and a pointer to the end:

typedef struct {
   nodeentry *front;
   nodeentry *rear;
} queue;
When the queue is empty, both of these pointers are NULL.

The two main operations are insert and remove:

/* insert a node at the end of the list */
int qinsert(stringval sval);
which returns true if successful and false otherwise.
/* remove a node from the front of the list */
stringval qremove(void);
Which returns the removed structure. It assumes that the queue is not empty.
We check for empty with:
int qempty(void);
which returns true if empty and false otherwise.

When writing the queue routines, you should think about the testing process.
One of the problems with a doubly linked list is that it can become inconsistent.
You might want to write a routine to check that the queue is valid:

int qcheck();
will return true if the queue is OK and false otherwise.
It will also print a message indicating what is wrong with the queue.

Think about what you need to verify for a doubly linked list to be consistent.



Assume that we have a queue object with the following definition:
static queue q = {NULL,NULL};
Here is a version of this funtion:
/* Return true if OK, false if bad */
int qcheck() {
   nodeentry *ne;
   int count;
   if ( (q.front == NULL) && (q.rear != NULL) ) {
      printf("front is NULL but rear is not\n");
      return 0;
   }
   else if ( (q.rear == NULL) && (q.front != NULL) ) {
      printf("rear is NULL but front is not\n");
      return 0;
   }
   else if (q.front == NULL) return 1;
   if (q.front->next != NULL) {
      printf("First node does not point forward to NULL\n");
      return 0;
   }
   if (q.rear->prev != NULL) {
      printf("Last node does not point back to NULL\n");
      return 0;
   }
   ne = q.rear;
   count = 0;
   while (ne->next != NULL) {
      if (ne->next->prev != ne) {
         printf("Node %d points to node which does not point back to it\n",
                count);
         return 0;
      }  
      ne = ne->next;
   }
   if (ne != q.front) {
      printf("Last node doing forward links is not pointed to by front\n");
      return 0;
   }
   return 1;
}
Another useful routine for debugging is one which prints out the queue:
void showqueue() {
   int count;
   nodeentry *ne;

   if (q.front == NULL) {
      printf("Queue is empty\n");
      return;
   }
   count = 0;
   ne = q.front;
   while (ne != NULL) {
      count++;
      ne = ne->prev;
   }
   printf("Number of entries in queue: %d\n",count);
   ne = q.front;
   count = 1;
   while (ne != NULL) {
      printf("%5d: %5d %s\n",count,ne->sval.val,ne->sval.str);
      ne = ne->prev;
      count++;
   }
}

Here is a program for testing the queue:

#include <stdio.h>
#include "queue.h"

static void checkit() {
   if (qcheck())
      printf("Queue is OK\n");
}

static void testremove() {
   stringval sval;
   printf("Before remove queue is\n");
   showqueue();
   printf("After remove\n");
   if (qempty()) 
      printf("Remove failed\n");
   else {
      sval = qremove();
      printf("Removed:  %5d %s\n",sval.val,sval.str);
   }
   showqueue();
   checkit();
   printf("\n");
}

static void testinsert(int val, char *str) {
   stringval sval;
   sval.val = val;
   sval.str = str;
   printf("Before insert queue is\n");
   showqueue();
   printf("After insert of %d %s\n",val,str);
   if (!qinsert(sval))
      printf("Insert failed\n");
   showqueue();
   checkit();
   printf("\n");
}
   
int main() {
   checkit();
   showqueue();
   printf("\n");

   testremove();
   testinsert(10,"This is a test");
   testinsert(20,"This is only a test");
   testinsert(30,"If this were a real queue, it would be used for something");
   testremove();
   testremove();
   testinsert(40,"OK, we are almost done");
   testremove();
   testremove();
   testremove();
   return 0;
}

Here is the output it generates:
Queue is OK
Queue is empty

Before remove queue is
Queue is empty
After remove
Remove failed
Queue is empty
Queue is OK

Before insert queue is
Queue is empty
After insert of 10 This is a test
Number of entries in queue: 1
    1:    10 This is a test
Queue is OK

Before insert queue is
Number of entries in queue: 1
    1:    10 This is a test
After insert of 20 This is only a test
Number of entries in queue: 2
    1:    10 This is a test
    2:    20 This is only a test
Queue is OK

Before insert queue is
Number of entries in queue: 2
    1:    10 This is a test
    2:    20 This is only a test
After insert of 30 If this were a real queue, it would be used for something
Number of entries in queue: 3
    1:    10 This is a test
    2:    20 This is only a test
    3:    30 If this were a real queue, it would be used for something
Queue is OK

Before remove queue is
Number of entries in queue: 3
    1:    10 This is a test
    2:    20 This is only a test
    3:    30 If this were a real queue, it would be used for something
After remove
Removed:     10 This is a test
Number of entries in queue: 2
    1:    20 This is only a test
    2:    30 If this were a real queue, it would be used for something
Queue is OK

Before remove queue is
Number of entries in queue: 2
    1:    20 This is only a test
    2:    30 If this were a real queue, it would be used for something
After remove
Removed:     20 This is only a test
Number of entries in queue: 1
    1:    30 If this were a real queue, it would be used for something
Queue is OK

Before insert queue is
Number of entries in queue: 1
    1:    30 If this were a real queue, it would be used for something
After insert of 40 OK, we are almost done
Number of entries in queue: 2
    1:    30 If this were a real queue, it would be used for something
    2:    40 OK, we are almost done
Queue is OK

Before remove queue is
Number of entries in queue: 2
    1:    30 If this were a real queue, it would be used for something
    2:    40 OK, we are almost done
After remove
Removed:     30 If this were a real queue, it would be used for something
Number of entries in queue: 1
    1:    40 OK, we are almost done
Queue is OK

Before remove queue is
Number of entries in queue: 1
    1:    40 OK, we are almost done
After remove
Removed:     40 OK, we are almost done
Queue is empty
Queue is OK

Before remove queue is
Queue is empty
After remove
Remove failed
Queue is empty
Queue is OK

Time to write int qempty(void),, int qinsert(stringval sval) and stringval qremove()

Here is what we did in class:

int qempty(void) {
    return q.front == NULL;
}

int qinsert(stringval sval) {
   nodeentry *entryp;
   nentryp = (nodeentry*)malloc(sizeof(nodeentry));
   if (nentryp == null)
       return 0;
   nentryp->sval = sval;
   nentryp->prev = NULL;
   nentryp->next = q.rear;
   q.rear = nentryp;
   if (q.front == NULL) 
       q.front = nentry;
   else
      q.rear->next->prev = nentryp;
   return 1;
}
The only thing left is to write stringval qremove(void)

/* This assumes the queue is not empty */
stringval qremove() {

}
This was written in class as follows:
stringval qremove() {
    stringval sval;
    sval = q.front -> sval;
    if (q.front -> prev == NULL) {
        free(q.front);
        q.front = NULL;
        q.rear = NULL;
        return sval;
    }
    q.front = q.front -> prev;
    free(q.front -> next);
    q.front -> next = NULL;
    return sval;
}


Here is information about the last programming assignment.

Here is some review material for the exam.


6.6 Table Lookup

This is essentially Programming Assignment 3.


6.8 Unions

Unions are like structures.
The difference is that only one member can hold a value.
The members may share the same storage.

Example:

union sigval {
   int   sival_int;
   void  *sigval_ptr;
}
This can hold either an integer or a pointer, but not both.
It is up to the application to know which one to use.


Next Topic: Input and Output