Week 02a: Dynamic Data Structures
Memory | 1/69 |
Reminder:
Computer memory … large array of consecutive data cells or bytes
|
|
C execution: Memory | 2/69 |
An executing C program partitions memory into:
malloc()
... C execution: Memory | 3/69 |
Exercise #1: Memory Regions | 4/69 |
int numbers[] = { 40, 20, 30 }; void insertionSort(int array[], int n) { int i, j; for (i = 1; i < n; i++) { int element = array[i]; while (j >= 0 && array[j] > element) { array[j+1] = array[j]; j--; } array[j+1] = element; } } int main(void) { insertionSort(numbers, 3); return 0; }
Which memory region are the following objects located in?
insertionSort()
numbers[0]
n
array[0]
element
Dynamic Data Structures |
Dynamic Memory Allocation | 7/69 |
So far, we have considered static memory allocation
int x;// 4 bytes containing a 32-bit integer value char *cp;// 4 bytes (on CSE machines) // containing address of a char typedef struct {float x; float y;} Point; Point p;// 8 bytes containing two 32-bit float values char s[20];// array containing space for 20 1-byte chars
... Dynamic Memory Allocation | 8/69 |
In many applications, fixed-size data is ok.
In many other applications, we need flexibility.
Examples:
char name[MAXNAME];// how long is a name? char item[MAXITEMS];// how high can the stack grow? char dictionary[MAXWORDS][MAXWORDLENGTH];// how many words are there? // how long is each word?
With fixed-size data, we need to guess sizes ("large enough").
... Dynamic Memory Allocation | 9/69 |
Fixed-size memory allocation:
Dynamic Data Example | 10/69 |
Problem:
6 25 -1 999 42 -16 64
How to define the vector?
... Dynamic Data Example | 11/69 |
Suggestion #1: allocate a large vector; use only part of it
#define MAXELEMS 1000// how many elements in the vector int numberOfElems; scanf("%d", &numberOfElems); assert(numberOfElems <= MAXELEMS);// declare vector and fill with user input int i, vector[MAXELEMS]; for (i = 0; i < numberOfElems; i++) scanf("%d", &vector[i]);
Works ok, unless too many numbers; usually wastes space.
Recall that assert()
... Dynamic Data Example | 12/69 |
Suggestion #2: use variables to give object sizes
// how many elements in the vector int numberOfElems; scanf("%d", &numberOfElems);// declare vector and fill with user input int i, vector[numberOfElems]; for (i = 0; i < numberOfElems; i++) scanf("%d", &vector[i]);
Produces compiler error (compiler needs to know object sizes)
... Dynamic Data Example | 13/69 |
Suggestion #3: create vector after count read in
#include <stdlib.h>// how many elements in the vector int numberOfElems; scanf("%d", &numberOfElems);// declare vector and fill with user input int i, *vector; size_t numberOfBytes; numberOfBytes = numberOfElems * sizeof(int); vector = malloc(numberOfBytes); assert(vector != NULL); for (i = 0; i < numberOfElems; i++) scanf("%d", &vector[i]);
Works unless the heap is already full (very unlikely)
Reminder: because of pointer/array connection &vector[i] == vector+i
The malloc() | 14/69 |
Recall memory usage within C programs:
... The malloc() | 15/69 |
malloc()
void *malloc(size_t n);
What the function does:
n
NULL
size_t
unsigned int
... The malloc() | 16/69 |
Example use of malloc
... The malloc() | 17/69 |
Things to note about void *malloc(size_t)
stdlib.h
(void *)
NULL
* sizeof(
)
Exercise #2: Dynamic Memory Allocation | 18/69 |
Write code to
float *matrix = malloc(m * n * sizeof(float)); assert(matrix != NULL);
4·mn bytes allocated
typedef struct { int day, month, year; } DateT; typedef struct { int hour, minute; } TimeT; typedef struct { char plate[7]; DateT d; TimeT t; } TicketT; TicketT *tickets = malloc(1000 * sizeof(TicketT)); assert(tickets != NULL);
28,000 bytes allocated
Exercise #3: Memory Regions | 20/69 |
Which memory region is tickets
*tickets
tickets
*tickets
malloc
... The malloc() | 22/69 |
malloc()
Things to note about objects allocated by malloc()
free()
malloc()
... The malloc() | 23/69 |
Usage of malloc()
int *vector, length, i; … vector = malloc(length*sizeof(int));// but malloc() might fail to allocate assert(vector != NULL);// now we know it's safe to use vector[] for (i = 0; i < length; i++) { … vector[i] … }
Alternatively:
int *vector, length, i; … vector = malloc(length*sizeof(int));// but malloc() might fail to allocate if (vector == NULL) { fprintf(stderr, "Out of memory\n"); exit(1); }// now we know its safe to use vector[] for (i = 0; i < length; i++) { … vector[i] … }
fprintf(stderr, …)
stderr
exit(v)
v
Memory Management | 24/69 |
void free(void *ptr)
malloc()
*ptr
*ptr
malloc()
free()
... Memory Management | 25/69 |
Warning! Warning! Warning! Warning!
Careless use of malloc()
free()
malloc()
free()
Must be very careful with your use of malloc()
free()
... Memory Management | 26/69 |
If an uninitialised or otherwise invalid pointer is used, or an array is accessed with a negative or out-of-bounds index, one of a number of things might happen:
segmentation fault
... Memory Management | 27/69 |
Given a pointer variable:
NULL
... Memory Management | 28/69 |
Typical usage pattern for dynamically allocated objects:
// single dynamic object e.g. struct Type *ptr = malloc(sizeof(Type)); assert(ptr != NULL);… use object referenced by ptr e.g. ptr->name … free(ptr);// dynamic array with "nelems" elements int nelems = NumberOfElements; ElemType *arr = malloc(nelems*sizeof(ElemType)); assert(arr != NULL);… use array referenced by arr e.g. arr[4] … free(arr);
Memory Leaks | 29/69 |
Well-behaved programs do the following:
malloc()
free()
free()
Such programs may eventually exhaust available heapspace.
Exercise #4: Dynamic Arrays | 30/69 |
Write a C-program that
Sidetrack: Standard I/O Streams, Redirects | 31/69 |
Standard file streams:
stdin
stdout
stderr
fprintf(stdout, …)
printf(…)
fprintf(stderr, …)
main(…)
stdin
stdout
stderr
... Sidetrack: Standard I/O Streams, Redirects | 32/69 |
The streams stdin
stdout
stderr
stdin
prompt$ myprog < input.data
stdout
prompt$ myprog > output.data
stderr
prompt$ myprog 2> error.data
Abstract Data Types |
Abstract Data Types | 34/69 |
Reminder: An abstract data type is …
... Abstract Data Types | 35/69 |
ADT interface provides
stack *
Stack as ADT | 36/69 |
Interface (in stack.h
// provides an opaque view of ADT typedef struct StackRep *stack;// set up empty stack stack newStack();// remove unwanted stack void dropStack(stack);// check whether stack is empty int StackIsEmpty(stack);// insert an int on top of stack void StackPush(stack, int);// remove int from top of stack int StackPop(stack);
ADT stack
Static/Dynamic Sequences | 37/69 |
Previously we have used an array to implement a stack
... Static/Dynamic Sequences | 38/69 |
Inserting a value into a sorted array (insert(a,&n,4)
... Static/Dynamic Sequences | 39/69 |
Deleting a value from a sorted array (delete(a,&n,3)
Dynamic Sequences | 40/69 |
The problems with using arrays can be solved by
Benefits:
Self-referential Structures | 41/69 |
To realise a "chain of elements", need a node containing
struct node { int data; struct node *next; };
... Self-referential Structures | 42/69 |
When defining self-referential types with typedef
typedef struct node { int data; struct node *next; } NodeT;
... Self-referential Structures | 43/69 |
Note that the following definition does not work:
typedef struct { int data; NodeT *next; } NodeT;
Because NodeT
next
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 Lists in C |
Linked Lists | 45/69 |
To represent a chained (linked) list of nodes:
next
NULL
... Linked Lists | 46/69 |
Linked lists are more flexible than arrays:
next
Memory Storage for Linked Lists | 47/69 |
Linked list nodes are typically located in the heap
... Memory Storage for Linked Lists | 48/69 |
Create a new list node:
NodeT *makeNode(int v) { NodeT *new = malloc(sizeof(NodeT)); assert(new != NULL); new->data = v;// initialise data new->next = NULL;// initialise link to next node return new;// return pointer to new node }
... Memory Storage for Linked Lists | 49/69 |
Exercise #5: Creating a linked list | 50/69 |
Write C-code to create a linked list of three nodes with values 1, 42 and 9024.
NodeT *list = createNode(1); list->next = createNode(42); list->next->next = createNode(9024);
Iteration over Linked Lists | 52/69 |
When manipulating list elements
p
NodeT *p
p->data
p->next
p
p
p
p
NULL
... Iteration over Linked Lists | 53/69 |
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 Lists | 54/69 |
... Iteration over Linked Lists | 55/69 |
... Iteration over Linked Lists | 56/69 |
Check if list contains an element:
int inLL(NodeT *list, int d) { NodeT *p; for (p = list; p != NULL; p = p->next) if (p->data == d)// element found return 1; return 0;// element not in list }
Print all elements:
showLL(NodeT *list) { NodeT *p; for (p = list; p != NULL; p = p->next) printf("%6d", p->data); }
Exercise #6: Traversing a linked list | 57/69 |
What does this code do?
1 NodeT *p = list; 2 while (p != NULL) { 3 printf("%6d", p->data); 4 if (p->next != NULL) 5 p = p->next->next; 6 else 7 p = NULL; 9 }
What is the purpose of the conditional statement in line 4?
Every second list element is printed.
If *p
p->next->next
The if-statement ensures that we do not attempt to assign an invalid address to p
Exercise #7: Traversing a linked list | 59/69 |
Rewrite showLL()
void printLL(NodeT *list) { if (list != NULL) { printf("%6d", list->data); printLL(list->next); } }
Modifying a Linked List | 61/69 |
Insert a new element at the beginning:
NodeT *insertLL(NodeT *list, int d) { NodeT *new = makeNode(d);// create new list element new->next = list;// link to beginning of list return new;// new element is new head }
Delete the first element:
NodeT *deleteHead(NodeT *list) { assert(list != NULL);// ensure list is not empty NodeT *head = list;// remember address of first element list = list->next;// move to second element free(head); return list;// return pointer to second element }
What would happen if we didn't free
head
... Modifying a Linked List | 62/69 |
Delete a specific element (recursive version):
NodeT *deleteLL(NodeT *list, int d) { if (list == NULL) {// element not in list return list; } else if (list->data == d) { return deleteHead(list);// delete first element } else {// delete element in tail list list->next = deleteLL(list->next, d); return list; } }
Exercise #8: Freeing a list | 63/69 |
Write a C-function to destroy an entire list.
Iterative version:
void freeLL(NodeT *list) {
NodeT *p;
p = list;
while (p != NULL) {
NodeT *temp = p->next;
free(p);
p = temp;
}
}
Why do we need the extra variable temp
Stack ADT Implementation | 65/69 |
Linked list implementation (stack.c
#include <stdlib.h> #include <assert.h> #include "stack.h" typedef struct node { int data; struct node *next; } NodeT; typedef struct StackRep { int height;
|
|
Doubly-linked Lists | 66/69 |
Doubly-linked lists are a variation on "standard" linked lists where each node has a pointer to the previous node as well as a pointer to the next node.
You can find one possible implementation of the above doubly-linked list ADT in the directory "dllist", under "Programs" for this lecture.
.
Summary: Memory Management Functions | 67/69 |
void *malloc(size_t nbytes)
nbytes
NULL
... Summary: Memory Management Functions | 68/69 |
void free(void *ptr)
malloc()
*ptr
*ptr
malloc()
free()
Summary | 69/69 |
Produced: 30 Nov 2018