Week 05


Week 051/48

Things to Note ...

In This Lecture ...

Coming Up ...


Nerds You Should Know #42/48

The next in a series on famous computer-related people ...
 
 
   

 
Nice hat/shirt ... but what has he done since 1971?


... Nerds You Should Know #43/48

Michael Hart

   
  • Founder of Project Gutenberg
    • books for everyone for free
    • only out-of-copyright books
    • available in plain ascii text
    • typed/scanned by volunteers
    • 99.9% accessible, 99.9% correct
  • Currently have > 49,000 texts online, e.g.

    - "Hamlet", "Macbeth", ... by William Shakespeare
    - "Faust", ... by Johann Wolfgang von Goethe
    - "The Art of War" by Sun Tzu
    - "The Magic Pudding" by Norman Lindsay
    - "CIA World Factbook", 1990-2010
    - "Chromosome Y", Human Genome Project


Dynamic Memory Allocation4/48

Statically allocated objects ...

Dynamically allocated objects ...


... Dynamic Memory Allocation5/48

Example: write a function lowerCase() to return a lower-cased version of a string

char *old = "This is MY string.";
char *new;

new = lowerCase(old);

print("%s\n%s\n", old, new);
// Output:
// This is MY string.
// this is my string.

Notes:


... Dynamic Memory Allocation6/48

Attempt #1: use a local string buffer

char *lowerCase(char *str) {
	char *cp;  // input string cursor
	char *op;  // output string cursor
	char out[BUFSIZ];  // output string buffer
	
	op = out;
	for (cp = str; *cp != '\0'; cp++) {
		*op = tolower(*cp);
		op++;
	}
	*op = '\0';
	return out;  // what is the precise effect?
}

BUG: the out variable is removed when lowerCase() returns.


... Dynamic Memory Allocation7/48

Attempt #2: user-supplied string buffer (different interface)

char *old = "This is MY string.";
char new[BUFSIZ];
...
lowerCase(old, new);
...
void lowerCase(char *in, char *out) {
	char *cp, *op = out;
	for (cp = in; *cp != '\0'; cp++) {
		*op = tolower(*cp);
		op++;
	}
	*op = '\0';
}

More common way of writing loop body:   *op++ = tolower(*cp);


... Dynamic Memory Allocation8/48

Attempt #3: dynamically allocated buffer

char *lowerCase(char *str) {
	char *cp;   // input string cursor
	char *op;   // output string cursor
	char *out;  // output string buffer
	
	out = (char *)malloc(strlen(str)+1);
	assert(out != NULL);
	op = out;
	for (cp = str; *cp != '\0'; cp++) {
		*op = tolower(*cp);
		op++;
	}
	*op = '\0';
	return out;  // what is the precise effect?
}


Memory Management9/48

void free(void *ptr)

Things to note:


... Memory Management10/48

Warning! Warning! Warning! Warning!

Careless use of malloc() / free() / pointers

Such errors are very difficult to track down and debug.

Be very careful with your use of malloc() / free() / pointers.


... Memory Management11/48

Given a pointer variable:


... Memory Management12/48

Typical usage pattern for dynamically allocated objects:

// single dynamic object e.g. struct
size_t size = sizeof(Type);
Type *ptr = (Type *)malloc(size);
assert(ptr != NULL);
... use object referenced by ptr e.g. ptr->name ...
free(ptr);

// dynamic array with "nelems" elements
int nelems = NumberOfElements;
size_t eSize = sizeof(ElemType);
ElemType *arr = (Type *)malloc(nelems*eSize);
assert(arr != NULL);
... use array referenced by arr e.g. arr[4] ...
free(arr);


Exercise: Flatten a 2d Array13/48

Implement a function

int *flattenArray(Matrix matrix, int m, int n)

which


Memory Leaks14/48

Well-behaved programs do the following:

A program which does not free() each object before the last reference to it is lost contains a memory leak.

Such programs may eventually exhaust available heapspace.


... Memory Leaks15/48

Example function with memory leak:


int median(int v[], int n) {
        int i, j, min, tmp, *sorted;

        sorted = (int *)malloc(n*sizeof(int));
        assert(sorted != NULL);

	for (i = 0; i < n; i++) { sorted[i] = v[i]; }

        for (i = 0; i < n; i++) {   // a simple sorting algorithm
                min = i;
                for (j = i+1; j < n; j++) {
                        if (sorted[j] < sorted[min]) { min = j; }
                }
		tmp = sorted[i];
                sorted[i] = sorted[min];
		sorted[min] = tmp;
        }
        return sorted[n/2];
}

The array referenced by sorted exists after function returns.


... Memory Leaks16/48

Example function without memory leak:


int median(int v[], int n) {
        int i, j, min, tmp, med, *sorted;

        sorted = (int *)malloc(n*sizeof(int));
        assert(sorted != NULL);

	for (i = 0; i < n; i++) { sorted[i] = v[i]; }

        for (i = 0; i < n; i++) {   // a simple sorting algorithm
                min = i;
                for (j = i+1; j < n; j++) {
                        if (sorted[j] < sorted[min]) { min = j; }
                }
		tmp = sorted[i];
                sorted[i] = sorted[min];
		sorted[min] = tmp;
        }
	med = sorted[n/2];
	free(sorted);
        return med;   // would return sorted[n/2]; work?
}

The array referenced by sorted is cleaned up before function returns.


Summary: Memory Management Functions17/48

void *malloc(size_t nbytes)

Things to note:


... Summary: Memory Management Functions18/48

void free(void *ptr)

Things to note:


... Summary: Memory Management Functions19/48

void *calloc(size_t nelems, size_t nbytes)


... Summary: Memory Management Functions20/48

void *realloc(void *ptr, size_t nbytes)


Exercise: Generic Matrix Library21/48

Use dynamically allocated memory to implement the following library of Matrix operations:

typedef struct { int nrows, ncols; float **data; } Matrix;
int readMatrix(Matrix *);
void printMatrix(Matrix);
void copyMatrix(Matrix, Matrix);
void transMatrix(Matrix, Matrix);
void addMatrix(Matrix, Matrix, Matrix);
void mulMatrix(Matrix, Matrix, Matrix);

Notes:


Dynamic Structures


Static/Dynamic Sequences23/48

We have studied arrays extensively

The "fixed size" aspect is a potential problem: The rigid sequence is another problems:


... Static/Dynamic Sequences24/48

Inserting a value into a sorted array (insert(a,&n,4)):

[Diagram:Pic/arrayInsert-small.png]


... Static/Dynamic Sequences25/48

Deleting a value from a sorted array (delete(a,&n,3)):

[Diagram:Pic/arrayDelete-small.png]


Exercise: Insert/Delete in Sorted Array26/48

Implement the above insert and delete operations:

// insert value v into array a of length n

void insert(int *a, int *n, int v);

// delete value v from array a of length n

void delete(int *a, int *n, int v);

What special cases do we need to consider?


Dynamic Sequences27/48

The problems with using arrays can be solved by

[Diagram:Pic/linkedList-small.png]

Benefits:


Self-referential Structures28/48

To realise a "chain of elements", need a node containing

In C, we can define such nodes as:

struct node {
	int data;
	struct node *next;
};


... Self-referential Structures29/48

When definining self-referential types with typedef

typedef struct node {
	int data;
	struct node *next;
} NodeT;

or

typedef struct node NodeT;
struct node {
	int data;
	NodeT *next;
};


... Self-referential Structures30/48

Note that the following definition does not work:

typedef struct {
	int data;
	NodeT *next;
} NodeT;

Because NodeT is not yet known (to the compiler) when we try to use it to define the type of the next field.

The following is also illegal in C:

struct node {
	int data;
	struct node recursive;    
};

Because the size of the structure would have to satisfy sizeof(struct node) = sizeof(int) + sizeof(struct node) = ∞.


Linked Lists31/48

To represent a chained (linked) list of nodes:

[Diagram:Pic/linkedList2-small.png]


... Linked Lists32/48

Linked lists are more flexible than arrays:

Disadvantages:


Memory Storage for Linked Lists33/48

Linked list nodes are typically located in the heap

Variables containing pointers to list nodes Pointers to the start of lists are often


... Memory Storage for Linked Lists34/48

[Diagram:Pic/linkedList3-small.png]


Iteration over Linked Lists35/48

When manipulating list elements

To iterate over a linked list:


... Iteration over Linked Lists36/48

Standard method for scanning all elements in a linked list:

NodeT *list;  // pointer to first Node in list
NodeT *p;     // pointer to "current" Node in list

p = list;
while (p != NULL) {
	... do something with p->data ...
	p = p->next;
}

// which is frequently written as

for (p = list; p != NULL; p = p->next) {
	... do something with p->data ...
}


... Iteration over Linked Lists37/48

[Diagram:Pic/listIterate1-small.png]


... Iteration over Linked Lists38/48

[Diagram:Pic/listIterate2-small.png]


Exercise: Print a List39/48

Write a function that will

Use the following function interface:

void printList(NodeT *list) { ... }


Exercise: Length of List 40/48

Write a function that will

Use the following function interface:

int length(NodeT *list) { ... }

Recall: To iterate over a linked list,


List Type41/48

In the previous examples, we define a list

Thus, a common typedef for a list type is:

typedef struct node {
	int data;
	struct node *next;
} NodeT;

typedef NodeT *List;

The previous functions could then be defined as:

void printList(List list) { ... }
int length(List list) { ... }


... List Type42/48

Common variations on basic List type ...

Store pointers to both first (head) and last (tail) nodes:

typedef struct { NodeT *head; NodeT *tail; } List;

Include a length counter:

typedef struct { NodeT *head; int length; } List;


List Operations43/48

A typical list datatype would involve operations:


... List Operations44/48

An important operation used by list operations:

Implementation:

NodeT *makeNode(int v) {
	NodeT *new;
	new = (NodeT *)malloc(sizeof(NodeT));
	assert(new != NULL);
	new->data = v;
	new->next = NULL;
	return new;
}


... List Operations45/48


NodeT *insertTail(NodeT *head, int d) {
   // create new node with data
   NodeT *new = makeNode(d);
   if (head != NULL) {            // check if at least 1 node
      NodeT *cur;
      cur = head;                 // start at the head
      while (cur->next != NULL) { // this must be defined
         cur = cur->next;
      }
      cur->next = new;            // cur->next was NULL
   } else {
      head = new;                 // if no list, list = new
   }
   return head;
}


... List Operations46/48

Another important list operation:

Implementation:

void freeList(NodeT *list) {
	NodeT *cur;

	cur = list;
	while (cur != NULL) {
		NodeT *tmp = cur->next;
		free(cur);
		cur = tmp;
	}
}


Exercise: Sorted List Type47/48

Implement a new SortedIntList data type

Implement a library based on this:

typedef ... SortedIntList;
SortedIntList mkEmpty();
void printList(SortedIntList);
SortedIntList insert(SortedIntList, int);
SortedIntList delete(SortedIntList, int);


Tips for Next Week's Lab48/48


Memory management, Linked lists


Produced: 19 Aug 2016