1
Chapter 16Chapter 16
Structures
2
StructuresStructures
• A structure is a collection of one or more variables, possibly of different types, grouped together under a single name for convenient handling
• Structures permit a group of related variables to be treated as a unit instead of as separate entities
• Variables in a structure are called fields of the structure
3
An ExampleAn Example
• The information for an employee:– Name– Address– Phone number– Age– Sex– Identification number– Salary
4
An ExampleAn Example
struct employee {
char name[40];
char address[100];
char phone_number[10];
int age;
int sex;
char id_number[10];
int salary;};
5
Operations on StructuresOperations on Structures
• The only legal operations on a structure:
– Copying it or assigning to it as a unit
– Accessing its fields with ‘.’ and field names
– Taking its address with ‘&’
6
An ExampleAn Example
(0,0)
(4,3)
struct point { int x; int y;} x, y, z;
struct point pt;struct point maxpt = {320, 200};
printf(“%d, %d”, pt.x, pt.y);
7
An ExampleAn Example
pt1
pt2
struct rec { struct point pt1; struct point pt2;};
struct rec screen;
printf(“%d”, screen.pt1.x);
8
Returning Structures Returning Structures
struct point makepoint(int x, int y){ struct point temp;
temp.x = x; temp.y = y; return temp;}
9
Returning StructuresReturning Structures
struct rect screen;struct point middle;struct point makepoint(int x, int y);
screen.pt1 = makepoint(0, 0);screen.pt2 = makepoint(320, 200);middle = makepoint((screen.pt1.x + screen.pt2.x)/2,
(screen.pt1.y + screen.pt2.y)/2);
10
Structures ParametersStructures Parameters
struct point addpoint(struct point p1, struct point p2){ p1.x += p2.x; p1.y += p2.y; return p1;} int ptinrect(struct point p, struct rect r){ return p.x >= r.pt1.x && p.x < r.pt2.x && p.y >= r.pt1.y && p.y < r.pt2.y;}
11
Structures and FunctionsStructures and Functions#define min(a, b) ((a) < (b) ? (a) : (b))#define max(a, b) ((a) < (b) ? (b) : (a))struct rect canonrect(struct rect r){ struct rect temp; temp.pt1.x = min(r.pt1.x, r.pt2.x); temp.pt1.y = min(r.pt1.y, r.pt2.y); temp.pt2.x = max(r.pt1.x, r.pt2.x); temp.pt2.y = max(r.pt1.y, r.pt2.y); return temp;}
12
Pointers to StructuresPointers to Structures
struct point origin, *pp;pp = &origin;printf(“%d, %d”, (*pp).x, (*pp).y);printf(“%d, %d”, pp->x, pp->y);
struct rect r, *rp = &r;r.pt1.xrp->pt1.x(r.pt1).x(rp->pt1).x
13
Counting Occurrences of Counting Occurrences of KeywordsKeywords
{“auto”, 0}
{“break”, 0}
{“case”, 0}
{“char”, 0}
…
{“volatile”, 0}
{“while”, 0}
“auto”
“break”
“case”
“char”
…
“volatile”
“while”
0
0
0
0
…
0
0
keyword keycount keytab
14
Arrays of StructuresArrays of Structures
char *keyword[NKEYS];int keycount[NKEYS];
struct key { char *word; int count;} keytab[NKEYS];
15
Arrays of StructuresArrays of Structures
struct key { char *word; int count;} keytab[ ] = { {“auto”, 0}, {“break”, 0}, {“case”, 0}, {“char”, 0},
… {“volatile”, 0}, {“while”, 0}};
16
Arrays of StructuresArrays of Structuresmain( ) { int n; char word[MAXWORD]; while (getword(word, MAXWORD) != EOF) if (isalpha(word[0])) if ((n= binsearch(word, keytab, NKEYS)) >= 0) keytab[n].count++; for (n = 0; n < NKEYS; n++) if (keytab[n].count > 0) printf("%4d %s\n", keytab[n].count, keytab[n].word); return 0;}
17
Arrays of StructuresArrays of Structures
int binsearch(char *word, struct key tab[ ], int n) { int cond, low = 0, high = n - 1, mid; while (low <= high) { mid = (low + high) / 2; if ((cond = strcmp(word, tab[mid].word)) < 0) high = mid -1; else if (cond > 0) low = mid + 1; else return mid; } return -1;}
18
Arrays of StructuresArrays of Structuresint getword(char *word, int lim) { int c; char *w = word; while (isspace(c = getch( ))) ; if (c != EOF) *w++ = c; if (!isalpha( c )) { *w = '\0'; return c; } for (; --lim > 0; w++) if (!isalnum(*w = getch( ))) { ungetch(*w); break; } *w = '\0'; return word[0];}
19
Pointers to StructuresPointers to Structuresmain( ) { char word[MAXWORD]; struct key *p; while (getword(word, MAXWORD) != EOF) if (isalpha(word[0])) if ((p= binsearch(word, keytab, NKEYS)) != NULL) p->count++; for (p = keytab; p < keytab + NKEYS; p++) if (p->count > 0) printf("%4d %s\n", p->count, p->word); return 0;}
20
Pointers to StructuresPointers to Structuresstruct key *binsearch(char *word, struct key *tab, int n) { int cond; struct key *low = &tab[0], *high = &tab[n], *mid; while (low < high) { mid = low + (high - low) / 2; if ((cond = strcmp(word, mid->word)) < 0) high = mid - 1; else if (cond > 0) low = mid + 1; else return mid; } return NULL;}
21
Counting Occurrences of All Counting Occurrences of All WordsWords
now is the time for all good men
to come to the aid of their party
22
Binary TreesBinary Trees
now
is the
men of timefor
party their togoodall
comeaid a pointer to the text of the worda count of the number of occurrencesa pointer to the left child nodea pointer to the right child node
23
Self-referential StructuresSelf-referential Structures
struct tnode { char *word; int count; struct tnode *left; struct tnode *right;}
24
Self-referential StructuresSelf-referential Structures
main( ) { char word[MAXWORD]; struct tnode *root = NULL;
while (getword(word, MAXWORD) != EOF) if (isalpha(word[0])) root = addtree(root, word); treeprint(root); return 0;}
25
Self-referential StructuresSelf-referential Structures
struct tnode *addtree(struct tnode *p, char *w) { int cond; if (p == NULL) { p = talloc( ); p->word = strdup(w); p->count = 1; p->left = p->right = NULL; } else if ((cond = strcmp(w, p->word)) == 0) p->count++; else if (cond < 0) p->left = addtree(p->left, w); else p->right = addtree(p->right, w); return p;}
26
Self-referential StructuresSelf-referential Structures
void treeprint(struct tnode *p) { if (p != NULL) { treeprint(p->left); printf("%4d %s\n", p->count, p->word); treeprint(p->right); }}
27
Self-referential StructuresSelf-referential Structuresstruct tnode *talloc(void) { return (struct tnode *) malloc(sizeof(struct tnode));}
char *strdup(char *s){ char *p; p = (char *) malloc(strlen(s) + 1); if (p != NULL) strcpy(p, s); return p;}
28
RecursionRecursion
• C functions may be used recursively; that is, a function may call itself either directly or indirectly
• When a function calls itself recursively, each invocation gets a fresh set of all the automatic variables
29
An Example - IterationAn Example - Iteration
#include <stdio.h>
int factorial(int n) { int product, i; product = 1; for (i = 1; i <= n; i++) product *= i; return product; }
30
An Example - RecursionAn Example - Recursion
#include <stdio.h>
int factorial(int n) { if (n = 0) { return 1; } else { return n * factorial(n – 1); } }
31
An Example - RecursionAn Example - Recursion
factorial(1)factorial(2) factorial(0)
112
n=2 n=2
n=1
n=0
n=2
n=1
32
An Example - IterationAn Example - Iteration
void itoa( int n, char s[ ] ) { int i, sign; if ((sign = n) < 0) n = -n; i = 0; do s[i++] = n % 10 + ‘0’; } while ((n /= 10) > 0); if (sign < 0) s[i++] = ‘-’; s[i] = ‘\0’; reverse(s); }
33
An Example - RecursionAn Example - Recursion
#include <stdio.h>
void printd( int n) { if (n < 0) { putchar(‘-’); n = -n; } if (n / 10) printd(n / 10); putchar(n % 10 + ‘0’); }
34
An Example - RecursionAn Example - Recursion
printd(12)printd(123) printd(1)
123
n=123 n=123
n=12
n=1
n=123
n=12
35
An Example - IterationAn Example - Iteration
int binsearch(char *word, struct key tab[ ], int n) { int cond, low = 0, high = n - 1, mid; while (low <= high) { mid = (low + high) / 2; if ((cond = strcmp(word, tab[mid].word)) < 0) high = mid -1; else if (cond > 0) low = mid + 1; else return mid; } return -1;}
36
An Example - RecursionAn Example - Recursionint binsearch(char *word, struct key tab[ ], int low, int high) { int cond, mid; if (low <= high) { return -1; } else { mid = (low + high) / 2; if ((cond = strcmp(word, tab[mid].word)) < 0) return binsearch(word, tab, low, mid - 1); else if (cond > 0) return binsearch(word, tab, mid + 1, high); else return mid; } }
37
RecursionRecursion
• Recursion provides no saving in storage, since the data for every invocation must be maintained
• Recursion provides no saving in execution time, since time for function invocations is higher than loops
• Recursive functions are more compact, much easier to write and understand
38
An Example - QueuesAn Example - Queues
• A queue is a waiting line. Customers go to the back of the waiting line and wait for their turn
• Some common operations on queues:– Create a new queue– Eliminate an existing queue– Add a customer to the end of the the queue– Remove a customer from the front of the queue– Determine how many customers are in the queue
39
First ImplementationFirst Implementation
• A queue is implemented as an array and the front of the queue is always the first element of the array– The length of the queue is statically determined
– Removing a customer is very inefficient
40
An ExampleAn Example
typedef struct queue { void *array[MAXSIZE]; int len;} *queueT;
array:len: 0
array: alen: 1
array: a blen: 2
array: a b clen: 3
array: b clen: 2
41
An ExampleAn ExamplequeueT newQueue(void){ queueT q; q = (queueT) malloc(sizeof(struct queue)); q->len = 0; return q;}
void freeQueue(queueT q) int queueLength(queueT q){ { free((char *) q); return q->len;} }
42
An ExampleAn Example
void enqueue(queueT q, void *obj){ if (q->len == MAXSIZE) { error(“enqueue called on a full queue”); } q->array[q->len++] = obj;}
43
An ExampleAn Example
void *dequeue(queueT q){ void *result; int i; if (q->len == 0) error(“dequeue called on an empty queue”); result = q->array[0]; for (i = 1; i < q->len; i++) q->array[i - 1] = q->array[i] ; q->len--; return result;}
44
Second ImplementationSecond Implementation
• A queue is implemented as a ring buffer and the front of the queue will change dynamically– The length of the queue is statically determined
– Removing a customer is very efficient
45
Ring BuffersRing Buffers
head tail
46
An ExampleAn Example
typedef struct queue { void *array[MAXSIZE]; int head; int tail;} *queueT;
array:head: 0 tail: 0
array: ahead: 0 tail: 1
array: a bhead: 0 tail: 2
array: a b chead: 0 tail: 3
array: b chead: 1 tail: 3
47
An ExampleAn ExamplequeueT newQueue(void){ queueT q; q = (queueT) malloc(sizeof(struct queue)); q->head = q->tail = 0; return q;}
void freeQueue(queueT q) int queueLength(queueT q){ { free((char *) q); return ((q->tail-q->head) %MAXSIZE) ;} }
48
An ExampleAn Example
void enqueue(queueT q, void *obj){ if (queueLength(q) == MAXSIZE) { error(“enqueue called on a full queue”); } q->array[q->tail++] = obj; q->tail %= MAXSIZE;}
49
An ExampleAn Example
void *dequeue(queueT q){ void *result; int i; if (queueLength(q) == 0) error(“dequeue called on an empty queue”); result = q->array[q->head++]; q->head %= MAXSIZE; return result;}
50
Third ImplementationThird Implementation
• A queue is implemented as a linked list and the front of the queue will change dynamically– The length of the queue is dynamically
determined
– Removing a customer is very efficient
51
Linked ListsLinked Lists
data data data data
52
An ExampleAn Example
head: NULLtail: NULL
ahead: tail:
a bhead: tail:
a b chead: tail:
b chead: tail:
typedef struct element { void *data; struct element *next;} *elementT;
typedef struct queue { elementT head; elementT tail; int len;} *queueT;
53
An ExampleAn Example
queueT newQueue(void){ queueT q; q = (queueT) malloc(sizeof(struct queue)); q->head = q->tail = NULL; q->len = 0; return q;}
54
An ExampleAn Example
void freeQueue(queueT q) int queueLength(queueT q){ { elementT e, pe; return q->len;
} e = q->head; while (e != NULL) { pe = e; e = e->next; free((char *) pe); } free((char *) q);}
55
An ExampleAn Examplevoid enqueue(queueT q, void *obj){ elementP e; e = (elementP) malloc(sizeof(struct element)); e->data = obj; e->next = NULL; if (q->len == 0) { q->head = q->tail = e; } else { q->tail->next = e; q->tail = e; } q->len++;}
56
An ExampleAn Examplevoid *dequeue(queueT q) { void *result; elementP e; if (q->len == 0) { error(“dequeue called on an empty queue”); } else { e = q->head; result = e->data; q->head = e->next; q->len--; if (q->len == 0) q->tail = NULL; free((char *) e); } return result;}
57
Macro ProcessingMacro Processing
/* macro definition */#define IN 1
#define min(a, b) ((a) < (b) ? (a) : (b))
/* macro expansion */state = IN;/* state = 1; */value = min(2, 3); /* value = ((2) < (3) ? (2) : (3)); */
58
Hash TablesHash Tables
name
defn
next
name
defn
next
name
defn
next struct nlist{ struct nlist *next; char *name; char *defn;}
59
The Hash FunctionThe Hash Function
#define HASHSIZE 101
static struct nlist *hashtab[HASHSIZE];
unsigned hash(char *s){ unsigned hashval; for (hashval = 0; *s != ‘\0’; s++) hashval = *s + 31 * hashval; return hashval % HASHSIZE;}
60
An ExampleAn Example
“IN”
“1”
next
name
defn
next
name
defn
next“min(a, b)”
“((a) < (b)) ? (a) : (b)”
name
defn
“OUP”
“2”
61
Lookup an ElementLookup an Element
struct nlist *lookup(char *s){ struct nlist *np;
for (np = hashtab[hash(s)]; np != NULL; np = np->next)
if (strcmp(s, np->name) == 0) return np; return NULL;}
62
Install an ElementInstall an Elementstruct nlist *install(char *name, char *defn) { struct nlist *np; unsigned hashval; if ((np = lookup(name)) == NULL) { np = (struct nlist *) malloc(sizeof(*np)); if (np == NULL || (np->name=strdup(name)) == NULL) return NULL; hashval = hash(name); np->next = hashtab[hashval]; hashtab[hashval] = np; } else free((void *) np->defn); if ((np->defn = strdup(defn)) == NULL) return NULL; return np;}
63
Syntax TreesSyntax Trees
if-stmt
expr stmt stmt
+
* 4
53
treenode
treenode
64
UnionsUnions
• A union is a variable that may hold (at different times) objects of different types and sizes
union u_tag {int ival;float fval;char *sval;
} u;
• The variable u will be large enough to hold the largest of the three types
65
UnionsUnions
• Members of a union may be accessed asunion-name.member
orunion-pointer->member
• A union may only be initialized with a value of the type of its first member
union u_tag u = 10;
66
An ExampleAn Example
struct treenode {int utype;union utag u;
} node;
67
An ExampleAn Example
/* If the variable utype is used to keep *//* track of the current type stored in u */
if (node.utype == INT) printf(“%d\n”, node.u.ival);else if (node.utype == FLOAT) printf(“%f\n”, node.u.fval);else if (node.utype == STRING) printf(“%s\n”, node.u.sval);else printf(“bad type %d in utype\n”, node.utype);
68
FlagsFlags
• A compiler needs to record information about identifiers– Whether it is a keyword– Whether it is static– Whether it is external
• The most compact way to encode such information is a set of one-bit flags in a single char or int
69
An ExampleAn Example
#define KEYWORD 01#define EXTERNAL 02#define STATIC 04
enum { KEYWORD = 01, EXTERNAL = 02, STATIC = 04};
flags |= EXTERNAL | STATIC; /* turn on */flags &= ~(EXTERNAL | STATIC); /* turn off */if ((flags & (EXTERNAL | STATIC)) == 0) …
70
Bit FieldsBit Fields
• A bit-field, or field for short, is a set of adjacent bits within a single implementation-defined storage unit
• The syntax of field definition and access is based on structures
71
An ExampleAn Example
struct { unsigned int is_keyword : 1; unsigned int is_external : 1; unsigned int is_static : 1;} flags;
flags.is_extern = flags.is_static = 1; /* turn on */flags.is_extern = flags.is_static = 0; /* turn off */if (flags.is_extern == 0 && flags.is_static == 0) …
72
Bit FieldsBit Fields
• Almost everything about fields is implementation-dependent
• Fields may be declared only as ints; for portability, specify signed or unsigned explicitly
• Fields need not be named; unnamed fields are used for padding
• The special width 0 may be used to force alignment at the next word boundary
73
Bit FieldsBit Fields
struct example {
unsigned int a : 13;
unsigned int : 3;
unsigned int c : 4;
} ;
struct example {
unsigned int a : 13;
unsigned int : 0;
unsigned int c : 4;
} ;
74
Abstract Data TypesAbstract Data Types
• A type defined in terms of its behavior rather than its representation is called an abstract data type
• The type string defined in strlib.h is an abstract data type, while the type string defined in string.h is not an abstract data type
75
Abstract Data TypesAbstract Data Types
• An abstract type is exported by an interface along with a collection of functions that define its behavior
• The representation is a property of the implementation
• As with any abstraction, it is appropriate to change the implementation as long as the interface remains the same queue: array, ring buffer, linked list
76
Abstract Data TypesAbstract Data Types
basic data types
operators: +, -, *, /, = basic data values: int,float, char
control statements: if,switch,while, for, do
composite data values:array, struct, union, pointer
functions
abstract data types
77
Computational ComplexityComputational Complexity
• The computation complexity of an algorithm is a proportional measure of running time in terms of the problem size
• Quadratic time: selection sort• Linear time: linear search, dequeue (via arra
y)• Logarithic time: binary search• Constant time: dequeue (via ring buffer or li
nked list)
78
Selection SortSelection Sort
void sortIntArray(int array[], int size){ int lh, rh;
for (lh = 0; lh < size; lh++) { rh = findSmallestInt(array, lh, size – 1); swap(array, lh, rh); }}
79
Selection SortSelection Sort
int findSmallestInt(int array[], int low, int high){ int i, spos;
spos = low for (i = low; i <= high; i++) { if (array[i] < array[spos]) spos = i; } return spos;}
80
Selection SortSelection Sort
The number of comparisons
= N + (N –1) + (N – 2) + … + 3 + 2 + 1
= (N2 + N) / 2
The performance of the selection sort
algorithm is N2
81
Merge SortMerge Sort
void sortIntArray(int array[], int n){ int i, n1, n2, *arr1, *arr2; if (n > 1) { n1 = n / 2; n2 = n – n1; arr1 = newArray(n1, int); arr2 = newArray(n2, int); copy(array, arr1, 0, n1) copy(array, arr2, n1+1, n2); sortIntArray(arr1, n1); sortIntArray(arr2, n2); merge(array, arr1, n1, arr2, n2); }}
82
Merge SortMerge Sortvoid merge(int array[], int arr1[], int n1, int arr2[], int n2){ int p, p1, p2; p = p1 = p2 = 0; while (p1 < n1 && p2 < n2) { if (arr1[p1] < arr2[p2]) { array[p++] = arr1[p1++]; } else { array[p++] = arr1[p1++]; } } while (p1 < n1) array[p++] = arr1[p1++]; while (p2 < n2) array[p++] = arr2[p2++];}
83
Merge SortMerge Sort
The depth of recursion = log2N
The number of comparisons = N log2N
The performance of the merge sort
algorithm is N log2N
84
ComparisonsComparisons
N N2 NLogN 10 100 33 100 10000 644 1000 1000000 996510000 100000000 132877
N Selection sort Merge sort 10 0.00013 0.00094 100 0.00967 0.012 1000 1.08 0.1410000 110.0 1.6
85
A Record-Based ApplicationA Record-Based Application
• A teaching tool that asks a series of questions so that previous answers determine the order of subsequent questions
• This tool must be able to– Ask the student a question– Get an answer from the student– Move on to the next question, the choice of
which depends on the student’s response
86
A Record-Based ApplicationA Record-Based Application
• This tool can be implemented as a set of functions. Each function asks a question, reads in an answer, and then calls another function appropriate to the answer the student supplies
• This program is difficult to change, in particular, for teachers who are not programmers
87
A Record-Based ApplicationA Record-Based Application
• Instead, we want to design a teaching tool that presents a programmed instruction course to the student but allows teachers without programming skills to supply the questions, expected answers, and cross-reference information so that your tool can present the questions in the appropriate order
88
A Record-Based ApplicationA Record-Based Application
• The best approach is to design your tool as a general tool that takes all data pertaining to the programmed instruction course from a file
• If we adopt this approach, the same program can present many different courses by using different data files
89
A Record-Based ApplicationA Record-Based Application
• What are the overall requirements of the general problem?
• How can you represent the data for courses in the context of your program?
• What should a course data file look like?• How do you convert the external representation
used in the data file into the internal one?• How do you write the program that manipulates
the database?
90
A Record-Based ApplicationA Record-Based Application
• Ask the student the current question.• Request an answer from the student• Look up the answer in a list of possibilities
provided for that question. If the answer appears in the list, consult the data structure to choose the next question. Otherwise, the student is informed of this fact and given another chance at the same question
91
Internal RepresentationInternal Representationdb db
title
questions
typedef struct { string title; questionT questions[MaxQuestions + 1];} *courseDB;
courseDB db;
92
Internal RepresentationInternal Representation
nAnswers
answersqtext typedef struct { string qtext[MaxLinesPerQuestion+1]; answerT answers[maxAnswersPerQuestion]; int nAnswers;} *questionT;
ans nextq
typedef struct { string ans; int nextq;} answerT;
93
Internal RepresentationInternal Representation
db
title
questions
nAnswers
answersqtext
94
External RepresentationExternal Representation
1True or false: The earth revolves around the sun.-----true: 2false: 3
2…
95
Process CourseProcess Coursevoid processCourse(courseDB course){ questionT q; int gnum; string ans; int index; qnum = 1; while (qnum != 0) { q = course->questions[qnum]; askQuestion(q); ans = ConvertToUpperCase(getline()); index = findAnswer(ans, q); if (index == -1) printf(“I don’t understand that.\n”); else qnum = q->answers[index].nextq; }}
96
Ask QuestionAsk Question
void askQuestion(questionT q){ int i;
for (i = 0; q->qtext[i] != NULL; i++) { printf(“%s\n”, q->qtext[i]); }}
97
Find AnswerFind Answer
int findAnwer(string ans, questionT q){ int i;
for (i = 0; i < q->nAnswers; i++) { if (stringEqual(ans, q->answers[i].ans)) return i; } return –1;}
98
Data-Driven DesignData-Driven Design
• Programs that control their entire operation on the basis of information from a database are said to be data-driven
• Data-driven programs are usually shorter, more flexible, and easier to maintain than programs that incorporate the same information directly into the program design