This section presents example solutions to the excercises of the previous lectures.
#ifndef __INTLIST_H #define __INTLIST_H typedef intlist_handle_struct *intlist_handle_t; intlist_handle_t intlist_create(); void intlist_destroy(intlist_handle_t this); int intlist_append(intlist_handle_t this, int data); int intlist_getFirst(intlist_handle_t this); int intlist_getNext(intlist_handle_t this); #endif
As some of you already figured out, the type definition is not really necessary. We could rather use the already known list_handle_t definition and just define new access methods. This interface looks exactly the same as that shown above besides that:
int intlist_append(list_handle_t this, int data) { int *elementp = (int *) malloc(sizeof(int)); *elementp = data; return list_append(this, elementp); }Note the use of list_append() to append the newly created element. One could say, that this new integer appending method adds additional functionality to list_append().
One possibility is to define a special value, say -1, to fulfill the function of a ``terminator''. However, this implies that this special value cannot be used as a normal value in the list.
Another approach is to change the prototypes of both functions to explicitly indicate the end of the list:
int intlist_getFirst(list_handle_t this, int *data); int intlist_getNext(list_handle_t this, int *data);
Both functions should return 0 (zero) as long as a value could be obtained and stored in *data. They return -1 otherwise.
Another method which is affected by this interface choice is intlist_destroy(). This function must now release the allocated memory prior to deleting the ``raw'' list:
void intlist_destroy(list_handle_t this) { int *elementp = (int *) list_getFirst(this); while (elementp) { free(elementp); elementp = (int *) list_getNext(this->_list); } list_destroy(this); /* Delete list portion */ }Again notice the use of the general list methods list_getFirst(), list_getNext() and list_destroy().
#define intlist_append(this, data) list_append(this, (void *) &data)This assumes, that data is of an arbitrary type. However, the replaced method must be able to build an address of the provided data element (as indicated by the ``&'' in front of data). We could now use this macro to append integer variables to the list:
int data = 1; list_handle_t intlist; intlist_append(intlist, data);
However, this approach introduces at least the following problems:
intlist_append(intlist, 2);This is not possible due to the address operator used in the macro: What is the address of a number? (Always remember the textual replacement definition of macros!)
void foo() { int data = 0; intlist_append(alist, data); } /* data is destroyed! */When function foo() terminates, the locally allocated memory is released, hence, the memory of data is lost. Consequently, the stored pointer points to memory which is no longer valid!
#ifndef __QUEUE_H #define __QUEUE_H typedef queue_handle_struct *queue_handle_t queue_handle_t queue_create(); void queue_destroy(queue_handle_t this); void *queue_get(queue_handle_t this); int queue_put(queue_handle_t this, void *data); #endif
struct queue_handle_struct { list_handle_t _list; /* A list is the queue */ };
void *queue_get(queue_handle_t this) { void *datap = list_getFirst(this->_list); list_delFromFront(this->_list); return datap; } int queue_put(queue_handle_t this, void *data) { return list_append(this->_list, data); }
mul(k) div(k) abs()
The operation mul does not require any precondition. That's similar to add and sub. The postcondition is of course res = N*k. The next operation div requires k to be not 0 (zero). Consequently, we define the following precondition: k not equal 0. The last operation abs returns the value of N if N is positive or 0 or -N if N is negative. Again it does not matter what value N has when this operation is applied. Here is its postcondition:
class Complex { attributes: Real real, imaginary methods: :=(Complex c) /* Set value to the one of c */ Real realPart() Real imaginaryPart() Complex +(Complex c) Complex -(Complex c) Complex /(Complex c) Complex *(Complex c) }
We choose the well-known operator symbols ``+'' for addition, ``-'' for subtraction, ``/'' for division and ``*'' for multiplication to implement the corresponding operations of the ADT Complex. Thus, objects of class Complex can be used like:
Complex c1, c2, c3 c3 := c1 + c2
You may notice, that we could write the addition statement as follows:
c3 := c1.+(c2)
You may want to replace the ``+'' with ``add'' to come to a representation which we have used so far. However, you should be able to understand that ``+'' is nothing more than a different name for ``add''.