This here the code for the program which counts the number of occurrences of all the words in some input . Taken from the book K and R. I pretty much understood everything except why is the author using strdup() anyway.Why can't we just assign(in the function addtree) p->word=w . In the structure tnode, word is clearly a pointer to a character and argument of addtree function is a character pointer.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define MAXWORD 100
#define BUFSIZE 100
struct tnode { /* the tree node: */
char *word; /* points to the text */
int count; /* number of occurrences */
struct tnode *left; /* left child */
struct tnode *right; /* right child */
};
struct tnode *addtree(struct tnode *, char *);
struct tnode *talloc(void);
void treeprint(struct tnode *);
void ungetch(int);
int getword(char *, int);
int getch(void);
char buf[BUFSIZE]; /* buffer for ungetch */
int bufp = 0; /* next free position in buf */
int main(void) { /* word frequency count */
struct tnode *root;
char word[MAXWORD];
root = NULL;
while(getword(word, MAXWORD) != EOF)
if(isalpha(word[0]))
root = addtree(root, word);
treeprint(root);
exit(0);
}
/* getword: get next word or character from input */
int getword(char *word, int lim) {
int c, getch(void);
void ungetch(int);
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];
}
/* addtree: add a node with w, at or below p */
struct tnode *addtree(struct tnode *p, char *w) {
int cond;
if(p == NULL) { /* a new word has arrived */
p = talloc(); /* make a new node */
p->word = strdup(w);
p->count = 1;
p->left = p->right = NULL;
} else if((cond = strcmp(w, p->word)) == 0)
p->count++; /* repeated word */
else if(cond < 0) /* less than into left subtree */
p->left = addtree(p->left, w);
else /* greater than into right subtree */
p->right = addtree(p->right, w);
return p;
}
/* talloc: make a tnode */
struct tnode *talloc(void) {
return(struct tnode *)malloc(sizeof(struct tnode));
}
/* treeprint: in-order print of tree p */
void treeprint(struct tnode *p) {
if(p != NULL) {
treeprint(p->left);
printf("%4d %s\n", p->count, p->word);
treeprint(p->right);
}
}
int getch(void) {
return (bufp > 0) ? buf[--bufp] : getchar();
}
void ungetch(int c) {
if(bufp >= BUFSIZE)
printf("ungetch: too many characters\n");
else
buf[bufp++] = c;
}
Because without strdup you just assign the address of the variable so both will point to the same memory (thus same data).
char *ptr2 = ptr1;
+----------+ +---------+
| PTR1 |---------->| VALUE |
+----------+ +---------+
^
+----------+ |
| PTR2 |----------------+
+----------+
While with strdup a new block of memory is allocated and the characters are copied into new one:
char *ptr2 = strdup(ptr1);
+----------+ +---------+
| PTR1 |---------->| VALUE |
+----------+ +---------+
+----------+ +---------+
| PTR2 |---------->| VALUE |
+----------+ +---------+
You see the difference?
#DCoder is right, which is really the answer hence this post.
The line where the word is first allocated is:
char word[MAXWORD];
The program keeps reeding into this very memory over and over again. Without copying this data for each node, they'd all point to this buffer.
Related
My program is from the C programming language book from K&R on section 6.5 where we are investigating self referential structures. Here, we would have to count the number of occurrences of words inputted into the terminal. We would have to construct a binary tree of words and keep track of the count. I am getting a segmentation Fault 11 error in the addtree function. I think p in that function is not pointing to the correct memory. But I am not sure how to fix it. Here is my code below:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#define MAXWORD 100
#define BUFSIZE 100
struct tnode{
char *word;
int count;
struct tnode *left; //left child. referential way of declaring
struct tnode *right;
};
struct tnode *addtree(struct tnode *, char *);
void treeprint(struct tnode *);
int get_word(char *, int);
struct tnode *talloc(void);
char *my_strdup(char *);
int main(){
struct tnode *root;
char word[MAXWORD];
while(get_word(word, MAXWORD) != EOF){
if(isalpha(word[0])){
printf("Still in main going in addtree function\n");
root = addtree(root, word);
}
}
treeprint(root);
return 0;
}
struct tnode *addtree(struct tnode *p, char *w){
int cond;
printf("Inside addtree function.\n");
if(p == NULL){ //a new word has arrived
p = talloc();
p->word = my_strdup(w);
p->count = 1;
p->left = p->right = NULL;
}else if((cond = strcmp(p->word, w))==0)
p->count++;
else if(cond < 0)
p->right = addtree(p->right, w);
else
p->left = addtree(p->left, w);
return p;
}
void treeprint(struct tnode *p){
if(p!=NULL){
printf("Coming inside treeprint statement.\n");
treeprint(p->left);
printf("%4d %s\n", p->count, p->word);
treeprint(p->right);
}
}
struct tnode *talloc(void){
return (struct tnode *) malloc(sizeof(struct tnode));
}
char *my_strdup(char *w){
char *p;
p = (char *) malloc(strlen(w)+1);
if(p!=NULL)
strcpy(p, w);
return p;
}
char buf[BUFSIZE]; //buffer for ungetch
int bufp = 0; //next free position in buf
int getch(void){
return (bufp>0) ? buf[--bufp] : getchar();
}
void ungetch(int c){
if(bufp >= BUFSIZE)
printf("ungetch: too many characters.\n");
else
buf[bufp++] = c;
}
//get next word or character from input
int get_word(char *word, int lim){
int c;
int getch(void);
void ungetch(int);
char *w = word;
while((isspace(c = getch())))
;
if(c != EOF){
*w++ = c;
}
printf("c: %d\n", c);
printf("\n*w: %c", *w);
if(!isalpha(c)){
*w = '\0';
return c;
}
for( ; --lim > 0; w++){
if(!isalnum(*w = getch())){
ungetch(*w);
break;
}
}
*w = '\0';
printf("word[0]: %c\n", word[0]);
return word[0];
}
It's crashing here:
}else if((cond = strcmp(p->word, w))==0)
on the first run, because root->word was never allocated/initialized.
I would strongly suggest, as you work your way through learning C, that you also learn to use a C debugger. Here is a great gdb tutorial. With a debugger, you will find the source problems like this almost instantly.
Here's what gdb output looks like if you try to run this program, for example:
(gdb) run
Starting program: /home/nick/a.out
hello darkness my old friend #<-- my keyboard input
c: 104
*w: ?word[0]: h
Still in main going in addtree function
Inside addtree function.
Program received signal SIGBUS, Bus error.
addtree (p=0x23792ac296e9f5c5, w=0x7f7fffff1680 "hello") at test.c:44
44 }else if((cond = strcmp(p->word, w))==0)
Current language: auto; currently minimal
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
The program below is supposed to count the words in a C file. If I define MAXWORD to be less than 169 I get a segmentation fault. I find this to be confusing as I never use more than 8 characters from that 'word' string I declare inside of main. Not sure where or what to look at next so any pointers (no pun intended) would be appreciated.
After I compile it with gcc wc.c getword.c -o wc I run ./wc < wc.c I get a segmentation fault but only when I set MAXWORD less than 169.
This is the wc.c file
#include "getword.h"
#include <stdlib.h>
#define MAXWORD 169
struct tnode {
char *word;
int count;
struct tnode *left;
struct tnode *right;
};
struct tnode *addtree(struct tnode *, char *);
void printtree(struct tnode *);
int main(void) {
char word[MAXWORD];
struct tnode *root;
while (getword(word, MAXWORD) != EOF) {
printf("word: %s\n", word);
if (isalpha(word[0]))
root = addtree(root, word);
}
printtree(root);
return 0;
}
struct tnode *talloc(void);
struct tnode *addtree(struct tnode *p, char *word) {
int cond;
if (p == NULL) {
p = talloc();
p->word = strdup(word);
p->count = 1;
p->left = p->right = NULL;
} else if ((cond = strcmp(word, p->word)) == 0)
p->count++;
else if (cond < 0)
p->left = addtree(p->left, word);
else
p->right = addtree(p->right, word);
return p;
}
struct tnode *talloc(void) {
return (struct tnode *) malloc(sizeof(struct tnode));
}
void printtree(struct tnode *p) {
if (p != NULL) {
printtree(p->left);
printf("%4d %s\n", p->count, p->word);
printtree(p->right);
}
}
And here's the getword.c file:
#include "getword.h"
#define STACKMAX 100
void skip_quote(char c);
void skip_comment(char c);
void skip_line(void);
int getch(void);
void ungetch(int);
int getword(char *word, int lim) {
int c;
char *w = word;
while (isspace(c = getch()))
;
if (c != EOF)
*w++ = c;
if (c == '#') {
skip_line();
return c;
}
if (c == '"' || c == '\'') {
skip_quote(c);
return c;
}
if (c == '/' && ((c = getch()) == '*' || c == '/')) {
skip_comment(c);
return c;
}
if (!isalpha(c)) {
*w = '\0';
return c;
}
for ( ; --lim > 0; w++)
if (!isalnum(*w = getch()) && *w != '_') {
ungetch(*w);
break;
}
*w = '\0';
return word[0];
}
void skip_quote(char type) {
int prev, current;
prev = type;
current = '\0';
while ((prev == '\\' || current != type) && prev != current) {
prev = current;
current = getch();
}
}
void skip_comment(char c) {
int prev;
prev = '\0';
if (c == '/')
skip_line();
else if (c == '*')
while (prev != '*' && (c = getch()) != '/')
prev = c;
}
void skip_line(void) {
while (getch() != '\n')
;
}
int cstack[STACKMAX];
int sp = 0;
int getch(void) {
return (sp > 0) ? cstack[--sp] : getchar();
}
void ungetch(int c) {
if (sp < STACKMAX)
cstack[sp++] = c;
else
printf("error: stack is full\n");
}
This is the header getword.h header file
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int getword(char *, int);
Why am I getting a segmentation fault here?
You invoked undefined behavior by using a value of an uninitialized variable having automatic storage duration root, which is indeterminate, in the main() function. Try initializing it before use, like:
struct tnode *root = NULL;
In your function
struct tnode *addtree(struct tnode *p, char *word) {
int cond;
if (p == NULL) {
you are using value of pointer p, but you pass unitialized pointer to it in main function:
root = addtree(root, word);
SOLUTION:
Initialize pointer:
struct tnode *root = NULL;
You said
I get a segmentation fault but only when I set MAXWORD less than 169.
and you ask
Do you know why it wasn't causing a segmentation fault when the
MAXWORD limit was 169 or higher?
The undefined behaviour happened independently of value of MAXWORD as you have used uninitialized pointer in all paths of execution of your code. It was undefined behaviour, so undefined behaviour is the answer to your question.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I have the basic knowledge of preprocessor.But i have never understood how it works.Can some body explain me how it works?I would prefer practical explanation.Thank you.
#define A 1234
Literally as far as the compiler is concerned, every instance of A is now 1234.
Its useful for code readability and modifieability. If you have a constant number for instance thats used in 7-8 different functions, you can #define it and use a token instead, and then change it in 1 place rather then 7-8.
Alternatives to using #define are using constant variables or enums.
#define myVar 755
const int myVar = 755;
enum{ myVar = 755 };
Are all functionally equivalent in most cases.
#define MY_MACRO my_var
Everytime your compilator finds "MY_MACRO" in your code while creating the binary code, it considers you wrote "my_var".
In one of my assignment i have implemented simple pre-processor using c.I have used the simple hash table to store the name and its definition.
for example,
#define a 4
so here a is name and 4 is its definition.
So both a and 4 will be stored in the hash table.
now whenever in program name 'a' is encountered then it will be simply replaced by 4.
i am posting my code in c for pre-processor.Which is very simple.But it would be very helpful for you to understand how pre-processor works.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define HASHSIZE 101
#define BUFSIZE 100
#define MAXWORD 100
struct tnode {
int loc[10]; // to store the locations at which words occur
int i; // to store the index of array loc
char *word;
int count;
struct tnode *left;
struct tnode *right;
};
static struct nlist *hashtab[HASHSIZE];
int getword(char *word, int lim);
struct tnode *addtree(struct tnode *, char *);
void treeprint(struct tnode *);
struct tnode *talloc(void);
struct nlist {
/* table entry: */
struct nlist *next;
/* next entry in chain */
char *name;
/* defined name */
char *defn;
/* replacement text */
};
char buf[BUFSIZE];
int bufp = 0;
unsigned hash(char *s);
struct nlist *lookup(char *s);
char *strdup1(char *);
struct nlist *install(char *name, char *defn);
int undef(char *s); // this function will remove entry
struct tnode *z[10]; // to store address of each node(word)
int total = 0; // index for array z
char s1[1000]; // to store entire input
static int q = 0; // index to s1
int main() {
struct nlist *pt;
char word[100];
char name1[10];
char defn1[10];
int i = 0;
int k = 0;
/*
#define pi 5 is entered then...*/
while (getword(word, MAXWORD) != EOF) {
if (isalnum(word[0]) && (i == 0)) {
if ((pt = lookup(&word[0])) !=
NULL) // if name1(pi) is already present then replace it with its definition name(5)
{
strcpy(word, (pt->defn)); // if pi is alerady there then replace name by its definition
// strcpy((pt->defn),word);
for (k = 0; word[k] != '\0'; k++, q++) {
s1[q] = word[k];
}
} else // otherwise print the word
{
for (k = 0; word[k] != '\0'; k++, q++) {
s1[q] = word[k];
}
}
}
if (isalnum(word[0])) {
if (i == 2) // copy word to defn1(i.e. defn1=5)
{
strcpy(defn1, word);
install(name1, defn1);
i = 0;
}
}
if (isalnum(word[0])) {
if (i == 1) // copy the word to name1(i.e. name1=pi)
{
strcpy(name1, word);
i++;
}
}
if (word[0] == '#') {
if ((strcmp(word, "#define")) == 0) // if #define found then increase i to get next word(i.e. pi)
{
i++;
} else {
for (k = 0; word[k] != '\0'; k++, q++) {
s1[q] = word[k];
}
}
}
}
// printf("%d",i);
// printf("%s %s",name,defn);
// printf("%s",pt->defn);
printf("%s", s1);
}
unsigned hash(char *s) {
unsigned hashval;
for (hashval = 0; *s != '\0'; s++)
hashval = *s + 31 * hashval;
return hashval % HASHSIZE;
}
/* lookup: look for s in hashtab */
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;
/* found */
return NULL;
/* not found */
}
/* install: put (name, defn) in hashtab */
struct nlist *install(char *name, char *defn) {
struct nlist *np;
unsigned hashval;
if ((np = lookup(name)) == NULL) { /* not found */
np = (struct nlist *)malloc(sizeof(*np));
if (np == NULL || (np->name = strdup1(name)) == NULL)
return NULL;
hashval = hash(name);
np->next = hashtab[hashval];
hashtab[hashval] = np;
} else
/* already there */
free((void *)np->defn);
/*free previous defn */
if ((np->defn = strdup1(defn)) == NULL)
return NULL;
return np;
}
char *strdup1(char *s) {
char *p;
/* make a duplicate of s */
p = (char *)malloc(strlen(s) + 1); /* +1 for '\0' */
if (p != NULL)
strcpy(p, s);
return p;
}
int undef(char *s) {
struct nlist *np1;
if ((np1 = lookup(s)) != NULL) {
hashtab[hash(s)] = NULL; // delete the entry in array hashtab associated to s
free(np1); // free memory allocated to s
return 1; // if sucessful then return 1 else 0
}
return 0;
}
int getword(char *word, int lim) {
int c, getch(void);
void ungetch(int);
char *w = word;
while (isspace(c = getch())) {
s1[q] = ' ';
q++;
} // takes the space in string s1
if (c != EOF)
*w++ = c;
if (!isalnum(c) && (c != '#')) /*takes all character except #,digit and number in s*/
{
if (c != EOF) {
s1[q] = c;
q++;
}
*w = '\0';
return c;
}
for (; --lim > 0; w++)
if (!isalnum(*w = getch())) {
ungetch(*w);
break;
}
*w = '\0';
return word[0];
}
int getch(void) /* get a (possibly pushed-back) character */
{
return (bufp > 0) ? buf[--bufp] : getchar();
}
void ungetch(int c)
/* push character back on input */
{
if (bufp >= BUFSIZE)
printf("ungetch: too many characters\n");
else
buf[bufp++] = c;
}
/* addtree: add a node with w, at or below p */
struct tnode *addtree(struct tnode *p, char *w) {
int cond;
if (p == NULL) {
/* a new word has arrived */
p = talloc();
/* make a new node */
p->word = strdup(w);
p->count = 1;
p->left = p->right = NULL;
} else if ((cond = strcmp(w, p->word)) == 0)
p->count++;
/* repeated word */
else if (cond < 0)
/* less than into left subtree */
p->left = addtree(p->left, w);
else
/* greater than into right subtree */
p->right = addtree(p->right, w);
return p;
}
/* talloc: make a tnode */
struct tnode *talloc(void) {
return (struct tnode *)malloc(sizeof(struct tnode));
}
void treeprint(struct tnode *p) {
int i = 0;
for (i = 0; i < total; i++)
printf("%s", z[i]->word);
}
The code is self explanatory.Mind you it is very simple pre-processor.
The purpose of a #define is to give a value or macro a simple name that you can use in your program. This is called a symbolic constant. In stead of having to remember the value or retype the macro everywhere you need it, you define it once and use the name wherever you need. Also, to define for example the size of something (array, buffer), you define it once. Should you later need to change the size, you only need to edit the definition. These are only some examples of #defines.
The preprocessor reads the program text and replaces the macros and constants with their actual values. If a part of the program text is enclosed in #ifdef...#endif (conditional compilation) and the #ifdef evalates to FALSE, then that part is left out. In the next step the actual compiler compiles the resulting program text into actual code.
I am trying to get a program to run that basically takes an input string and sends it through some code and sorts it using linked lists alphabetically. I have figured out how to make this work using manual (in the actual code) text input, but I cannot get it to work when I am trying to take input from the user.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct llist {
char *value;
struct llist *next;
};
int compare (struct llist *one , struct llist *two)
{
return strcmp(one->value, two->value);
}
void add(struct llist **pp, char *value, int (*cmp)(struct llist *l, struct llist *r)) {
struct llist *new;
new = malloc(sizeof(*new));
new->value = value;
for ( ; *pp != NULL; pp = &(*pp)->next) {
if (cmp(*pp, new) > 0 ) break;
}
new->next = *pp;
*pp = new;
}
void display(struct llist *ptr) {
for (; ptr != NULL; ptr = ptr->next) {
printf("%s\n", ptr->value);
}
}
int main(void) {
struct llist *root = NULL;
char string;
printf("Please enter a string to be sorted alphabetically and displayed with a character count: ");
string = getchar();
while (string != 10){
add(&root,&string, compare);
string = getchar();
}
display(root);
return 0;
}
looking in the main function I'm quite certain it has something to do with getchar and the fact that it reads characters in as ints, but I can't figure out how to fix this, the output is just a bunch of empty lines. but when the while loop is removed and the strings are entered like below in main, it works fine? why is this so?
int main(void) {
struct llist *root = NULL;
char string;
printf("Please enter a string to be sorted alphabetically and displayed with a character count: ");
add(&root,"t", compare);
add(&root,"h", compare);
add(&root,"i", compare);
add(&root,"s", compare);
add(&root,"m", compare);
add(&root,"y", compare);
add(&root,"t", compare);
add(&root,"e", compare);
add(&root,"x", compare);
add(&root,"t", compare);
display(root);
return 0;
}
the output is now
e
h
i
m
s
t
t
t
x
y
which is correct,
Can anyone help me?
When you write "t" in the example, you get the address of an array. That address is different for each value you pass to add. However, the address of string does not change, and you are setting new->value to the same thing each time you call add. Instead of new->value = value, try new->value = *value (and all of the associated changes necessary).
There are not too many changes needed. Note that terminating after a single string of input is not very nice behavior. A good exercise would be to write a destructor that tears down the string after the first line and then sorts the next. Another nice exercise would be to sort arguments if any are passed, reading stdin if no arguments are given.
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct llist {
char value; /* CHANGE */
struct llist *next;
};
typedef int (*compar)( struct llist *one , struct llist *two );
int
compare( struct llist *one , struct llist *two )
{
return tolower( one->value ) > tolower( two->value ); /* CHANGE */
}
void *
xmalloc( size_t s )
{
void *v = malloc( s );
if( v == NULL ) {
perror( "malloc" );
exit( EXIT_FAILURE );
}
return v;
}
void
add( struct llist **pp, char value, compar cmp ) /* CHANGE */
{
struct llist *new;
new = xmalloc( sizeof *new ); /* Check for errors */
new->value = value; /* CHANGE the type of value above */
for( ; *pp != NULL; pp = &(*pp)->next ) {
if( cmp( *pp, new ) > 0 )
break;
}
new->next = *pp;
*pp = new;
}
void
display( struct llist *ptr )
{
for( ; ptr != NULL; ptr = ptr->next ) {
putchar( ptr->value ); /* CHANGE */
}
putchar( '\n' );
}
int
main( void )
{
struct llist *root = NULL;
char string;
while( (string = getchar()) != '\n' ) { /* Optional CHANGE (1) */
add( &root,string, compare );
}
display(root);
return 0;
}
/*
* (1) Using "'\n'" instead of 10 is necessary for readability,
* portability, and sanity of future maintainers.
*
* Writing getchar() only once is cleaner.
*/
With your hard coded entry code you are passing in string's to add while in your user input code you are only passing in a pointer to a single character. A char* is not necessarily a string in C, it may point to a string, but it doesn't have to.
In C a string is a buffer of characters that ends with a null zero, the character '\0' (which is usually just the value 0). When you are using getchar your are passing a pointer to a character - and that character does not have a null zero after it, so it is not a valid C string.
If you want to keep your code using getchar you need to use a buffer that will store a null zero after that character. You can do that will a small array as below:
int main(void) {
struct llist *root = NULL;
char string[2];
string[1] = '\0'; // Ensure that we have a null terminated string
printf("Please enter a string to be sorted alphabetically and displayed with a character count: ");
string[0] = getchar();
while (string != '\n'){
add(&root,string, compare);
string[0] = getchar();
}
display(root);
return 0;
}
Cleaned-up a few things
1) As #shf301 siad, need to pass a string, not just a pointer to a single char.
2) Need to create copies of the string - notice strdup.
3) While loop should break on '\n and EO and maybe '\0. getchar() returns an int for a reason: EOF and 0 to UCHAR_MAX (e. g. 255).
4) Should free() resources at the end - not shown.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
// In case you library does not have strdup() ...
char *strdup(const char *s) {
if (s == NULL) return NULL;
size_t siz = strlen(s) + 1;
char *y = malloc(siz);
if (y != NULL) {
memcpy(y, s, siz);
}
return y;
}
struct llist {
char *value;
struct llist *next;
};
int compare(struct llist *one, struct llist *two) {
return strcmp(one->value, two->value);
}
void add(struct llist **pp, char *value,
int(*cmp)(struct llist *l, struct llist *r)) {
struct llist *new;
new = malloc(sizeof(*new));
new->value = strdup(value);
for (; *pp != NULL; pp = &(*pp)->next) {
if (cmp(*pp, new) > 0)
break;
}
new->next = *pp;
*pp = new;
}
void display(const struct llist *ptr) {
for (; ptr != NULL; ptr = ptr->next) {
printf("%s\n", ptr->value);
}
}
int main(void) {
struct llist *root = NULL;
char string[2];
string[1] = '\0';
printf("Please enter a string to be sorted alphabetically"
" and displayed with a character count: ");
int ch;
while ((ch = getchar()) != EOF && ch != '\n' && ch != '\0') {
string[0] = ch;
add(&root, string, compare);
}
display(root);
return 0;
}
I am using visual leak detector in visual studio and It detects some leaks when i run the program. Basically there are two leaks in the program one is detected on the malloc'd structure *p and the other is detected in its members (p->word).
I read all about your discussions about freeing memory in a malloc'd structure. The problem is I can't seem to find a place where i can free the memory especially when this is a recursive structure.
Here is the code
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <vld.h>
#define MAXWORD 100
struct tnode { /*the tree node */
char *word; /* points to itself */
int count; /* number of occurences */
struct tnode *left; /* left child */
struct tnode *right; /* right child */
};
struct tnode *addtree (struct tnode *, char *);
struct tnode *talloc(void);
void treeprint (struct tnode *);
char *strdupli (char *s);
int getword (char *, int);
void strcopy (char *, const char *);
int main(void)
{
struct tnode *root;
char word[MAXWORD];
root = NULL;
while(getword(word, MAXWORD) != EOF)
if( isalpha (word[0]))
root = addtree(root, word);
treeprint(root);
return 0;
}
int getword(char *word, int lim)
{
int c, getch(void);
void ungetch(int);
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];
}
struct tnode *addtree (struct tnode *p, char *w)
{
int cond;
if (p == NULL) { /* a new word has arrived*/
p = talloc(); /* make a new node */
p->word = strdupli(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;
}
void treeprint (struct tnode *p)
{
if(p != NULL) {
treeprint(p->left);
printf("%4d %s\n", p->count, p->word);
treeprint(p->right);
}
}
/* talloc: make a tnode */
struct tnode *talloc(void)
{
return (struct tnode *) malloc(sizeof(struct tnode));
}
char *strdupli (char *s) /* make a duplicate of s */
{
char *p;
p = (char *) malloc (strlen(s) + 1);
if (p != NULL)
strcopy(p, s);
return p;
}
void strcopy (char *s, const char *t)
{
while ((*s++ = *t++) != '\0')
;
}
#define BUFSIZE 100
char buf[BUFSIZE]; /* buffer for ungetch */
int bufp = 0; /* next free position in buf */
int getch(void) /* get a (possibly pushed-back) character */
{
return (bufp > 0) ? buf[--bufp] : getchar();
}
void ungetch(int c) /* push character back on input */
{
if (bufp >= BUFSIZE)
printf("ungetch: too many characters\n");
else
buf[bufp++] = c;
}
And you need to look at *addtree function
To fix that, create a recursive function who looks like your print function to free you tree.
An example :
void free_tree (struct tnode *p)
{
if(p != NULL) {
free_tree(p->left);
free_tree(p->right);
free( p->word );
free( p );
}
}
You can put it in the main after treeprint :
int main(void)
{
...
treeprint(root);
free_tree( root );
// ^^^^^^^^^^^^^^^
return 0;
}
In situations like this, you write a recursive function to free the tree when you're done with it. In keeping with the style of this code:
void tfree(struct tnode *p)
{
if (p != NULL) {
tfree(p->left);
tfree(p->right);
free(p->word);
free(p);
}
}
Note that malloced memory that's still accessible at the end of the program isn't really a leak, since the operating system will clean it up. Some interesting discussion here, in particular this answer.
OF course, it's good style to free memory once you're done with it, but I would be more concerned with memory that is allocated but now unreachable.