210 likes | 227 Views
Learn to implement self-referential structs for a binary tree in C, including code examples, declarations, and prototypes. Understand pointers and recursive functions for struct nodes.
E N D
Homework / Quiz • Exam 2 • Solutions Posted • Questions? • Continuing K&R Chapter 6 • HW6 is on line – due class 22
Self Referential structs, K&R 6.5 • Look at this binary tree structure (Pg. 139) now is the for men of time all good party their to aid come
Self Referential structs struct tnode { /* the tree node struct */ char *word; /* points to the word at this node */ int count; /* has a count of occurances */ struct tnode *left; /* a word < one at this node */ struct tnode *right; /* a word > one at this node */ }; • OK to have a pointer to a struct of same type in its own definition • NOT OK to have a struct itself in its own definition!
Tree of struct tnodes tnode *root char *word int count tnode *left tnode *right keyword \0 … char *word int count tnode *left tnode *right … … … char *word int count tnode *left tnode *right … …
Self Referential structs • Each node is a struct tnode with a string value and a count of occurrences • Each node also contains a pointer to a left child and a right child • Each child is another struct tnode
Declarations / Prototypes #include <stdio.h> #include <ctype.h> (see functions in App. B2, pp. 248-249, K&R) #include <string.h> (see functions in App. B3, pp. 249-250, K&R) #include “getch.h” #define MAXWORD 100 int getword(char *word, int lim); struct tnode *addtree(struct tnode *, char *); void treeprint(struct tnode *);
Main program int main( ) { struct tnode *root; /* ptr to root for binary tree */ char word[MAXWORD]; /* word to get from stdin */ root = NULL; /* initially, no tnodes in tree */ /* OK that root pointer points to NULL initially */ while (getword(word, MAXWORD) != EOF) /* getword from stdin */ if (isalpha(word[0])) /* from ctype.h library */ root = addtree(root, word); /* expect not NULL anymore */ treeprint(root); /* print it out in order */ return 0; }
treeprint ( ) • To print out tree contents in order: void treeprint (struct tnode *p) { if (p != NULL) { treeprint (p->left); printf (%4d %s\n”, p->count, p->word); treeprint (p->right); } }
getword ( ) int getword(char *word, int lim) { int c; char *w = word; while isspace(c = getch()) /* skip spaces; c first non-space */ ; if (c != EOF) *w++=c; /* c might be EOF here (empty word) */ if(!isalpha(c)) { /* c (identifier) starts with alpha */ *w = '\0'; return c; /* if not, word empty */ } for( ; --lim > 0; w++) if (!isalnum(*w = getch())) { /* identfier alpha or digits */ ungetch(*w); break; /* e.g., + might be needed later */ } *w = '\0'; return word[0]; }
addtree ( ) struct tnode *talloc(void); /* allocate. return ptr to tnode */ char *strdup(char *); /* allocate space, copy word there */ struct tnode *addtree(struct tnode *p, char *w) { int cond; /*for empty tree, root is NULL */ if (p == NULL) { /* nothing at this node yet */ p = talloc(); /* allocate a new node */ p->word = strdup(w); /* allocate space, copy word there */ p->count = 1; /* count is 1 */ p->left = p->right = NULL; /* this works; see precedence */ } else if ((cond = strcmp(w, p->word)) == 0) /* string.h */ p->count++; /* repeated word, increment count */ else if (cond < 0) /* note cond remembers strcmp above */ p->left = addtree(p->left, w); /* less: go left subtree */ else /* more, go to right subtree */ p->right = addtree(p->right, w); return p; }
addtree( ) • Note, that addtree is recursive. If it adds a node, it might need to add root or any left or right child anywhere down the tree • We pass struct tnode *p as first argument, and also pass back the same type - always a pointer to the node at the current recursive nesting level • Any time a new node is created: root, p->left or p->right in node above will be set for the FIRST time based on return value from addtree
addtree ( ) • When main calls addtree, the return sets root value each time even if the root already pointed to tnode • Allows for possible addition of a pointer to a node • Why do we need to return a struct tnode *p when we have struct tnode *p as an argument? • Can't we just set it via argument, return void from addtree, and write (in main)? addtree(root, word); instead of root = addtree(root, word);
addtree ( ) • No, root is already a pointer, struct tnode *, but it is the pointer root itself that must be modified • To modify root, we would need to declare addtree with a pointer to a struct tnode * as the first arg: void addtree(struct tnode **pptr, char *w) { struct tnode *p = *pptr; • Call it from main recursively by invocation: addtree(&root, word); • Then, addtree could set the value for root directly *pptr = p; • Same is true when adding a left or right child
talloc ( ) • How to write talloc to allocate a tnode and return a pointer. struct tnode *talloc(void) { return (struct tnode *) malloc (sizeof(struct tnode)); }
strdup ( ) • strdup ( ) creates space for a character string, copies a word into it (up to null terminator) and returns the char pointer char *strdup(char *s) { p = (char *) malloc (strlen(s)+1); /* +1 for '\0' */ if (p != NULL) /* if malloc didn’t fail */ strcpy(p, s); /* library function in string.h */ return p; }
Intro to HW6 • Memory model for malloc( ) and free( ) Before any calls to alloc( ): allocbuf: After two calls to alloc( ) and before call to freef( ): allocbuf: After call to freef( ) on first block alloc gave out: allocbuf: Free Free In Use In Use Free In Use Free
Intro to HW6 • Fragmentation with malloc( ) and free( ) Before call to alloc( ) for a large memory block: allocbuf: alloc can NOT provide a large contiguous block of memory - even though there is theoretically sufficient free memory! Memory is fragmented!! In use Free In use Free In use Free In use
Intro to HW6 • Not possible to “defragment” allocbuf memory after the fact as you would do for a disk • On a disk, the pointers to memory are in the disk file allocation table and can be changed • With malloc, programs are holding pointers to memory they own - so can’t be changed!! • Can only defragment as blocks are freed by using program
Intro to HW6 • Initial structures in allocbuf[] • Structures after two calls to alloc() blockl blockr Free Memory blockl blockr blockl blockr blockl blockr Free Memory Inuse 2 Inuse 1
Intro to HW6 • Structures after free call on first block only • Structures after free call on second block only blockl blockr blockl blockr blockl blockr Free Memory Inuse 2 Free Memory blockl blockr blockl blockr Free Memory Inuse 1 Note effect of coalesce!
Intro to HW6 • Four possible states for block being freed • Between two blocks that are still in-use • Above a free block and below an in-use block • Above an in-use block and below a free block • Above a free block and below a free block • When freeing a block, coalesce with adjacent free blocks to avoid unnecessary fragmentation of allocbuf (allows alloc to serve later requests for large amounts of memory)