How preprocessor (i.e.#define) works? [closed] - c

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.

Related

My linked list is printing the last word in my text file for all the elements

This code is reading a text file and inserting each word into the linked list.
I am new to linked lists and have been working on this for four hours now and cannot for the life of me figure this out.
So what is going on here? I have checked every way I know how to, and for the life of me cannot get the linked list to print correctly. I believe it has something to do with the push/append functions. Somehow it is overwriting everything previously in the linked list. Maybe the printlist function is overwriting everything but I don't see how it could possibly be doing that.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
// A complete working C program to demonstrate all insertion methods
// on Linked List
// A linked list node
struct Node;
void push(struct Node **head_ref, char *new_data);
void insertAfter(struct Node *prev_node, char *new_data);
void append(struct Node **head_ref, char *new_data);
void printList(struct Node *node);
int LINECOUNT(FILE *(*stream), char *filename);
struct Node {
char *data;
struct Node *next;
};
/* Given a reference (pointer to pointer) to the head of a list and
an int, inserts a new node on the front of the list. */
void push(struct Node **head_ref, char *new_data) {
/* 1. allocate node */
struct Node* new_node = (struct Node *)malloc(sizeof(struct Node));
/* 2. put in the data */
new_node->data = new_data;
printf("push data:%s ", new_data);
/* 3. Make next of new node as head */
new_node->next = (*head_ref);
/* 4. move the head to point to the new node */
(*head_ref) = new_node;
}
/* Given a reference (pointer to pointer) to the head
of a list and an int, appends a new node at the end */
void append(struct Node **head_ref, char *new_data) {
/* 1. allocate node */
struct Node* new_node = (struct Node *)malloc(sizeof(struct Node));
struct Node *last = *head_ref; /* used in step 5*/
/* 2. put in the data */
new_node->data = new_data;
printf("push data:%s ", new_data);
/* 3. This new node is going to be the last node, so make next of
it as NULL*/
new_node->next = NULL;
/* 4. If the Linked List is empty, then make the new node as head */
if (*head_ref == NULL) {
*head_ref = new_node;
return;
}
/* 5. Else traverse till the last node */
while (last->next != NULL)
last = last->next;
/* 6. Change the next of last node */
last->next = new_node;
return;
}
// This function prints contents of linked list starting from head
void printList(struct Node *node) {
while (node != NULL) {
printf(" %s ", node->data);
node = node->next;
}
}
int LINECOUNT(FILE *(*stream), char *filename) {
int size = 0;
size_t chrCount;
char *text;
if ((*stream = fopen(filename, "r")) == NULL) {
printf("LC Could not open hw8 data file.\n");
exit(0);
}
while (1) {
text = NULL;
getline(&text, &chrCount, *stream);
free(text); /*free text*/
if (feof(*stream))
break;
size++;
}
rewind(*stream);
return size;
}
/*int wordCount(FILE *(*stream), char *filename, int lineCount) {
char ch;
int wordcount = 0;
int charcount = 0;
*stream = fopen(filename, "r");
int x = 0;
int lineArr[lineCount];
for (int i = 0; i < lineCount; i++) {
lineArr[i] = 0;
}
if (*stream) {
while ((ch = getc(*stream)) != EOF) {
if (ch != ' ' && ch != '\n') {
charcount++;
}
if (ch == ' ' || ch == '\n') {
wordcount++;
lineArr[x] = lineArr[x] + 1;
}
if (ch == '\n') {
x++;
}
}
if (charcount > 0) {
wordcount++;
charcount++;
}
} else {
printf("Failed to open the file\n");
}
// rewind(*stream);
return lineArr;
}*/
int main(void) {
char *fn = "hw8data.txt";
int lineCount;
FILE *stream;
lineCount = LINECOUNT(&stream, fn);
//int lineArr[lineCount];
//int lineArr[];//lineArr[0] = 4 would say the first line has 4 words. using this data for strtok
//lineArr = wordCount(&stream, fn, lineCount);
//-------------------------------------
char ch;
int wordcount = 0;
int charcount = 0;
stream = fopen("./hw8data.txt", "r");
int x = 0;
int lineArr[lineCount];
for (int i = 0; i < lineCount; i++) {
lineArr[i] = 0;
}
if (stream) {
while ((ch = getc(stream)) != EOF) {
if (ch != ' ' && ch != '\n') {
charcount++;
}
if (ch == ' ' || ch == '\n') {
wordcount++;
lineArr[x] = lineArr[x] + 1;
}
if (ch == '\n') {
x++;
}
}
//if (charcount > 0) { wordcount++; charcount++; }
} else {
printf("Failed to open the file\n");
}
/* Start with the empty list */
struct Node *head = NULL;
rewind(stream);
char *sArr = malloc(42 * sizeof(char));
fscanf(stream, "%s ", sArr);
printf("%s ", sArr);
push(&head, sArr);
fscanf(stream, "%s ", sArr);
printf("%s ",sArr);
append(&head, sArr);
printList(head);
return 0;
}
char* sArr=malloc(42*sizeof(char));
fscanf(stream,"%s ",sArr);
printf("%s ",sArr);
push(&head,sArr);
fscanf(stream,"%s ",sArr);
printf("%s ",sArr);
append(&head,sArr);
You add the same value to the list twice, the value you got back from your one and only call to malloc. If you want two nodes to hold different values, don't add the same value twice. One ugly fix is if after push(&head,sArr) you add another sArr = malloc(42*sizeof(char));. That way, your call to append will add a different value to the list.
If you don't see this, add code to output the value of node->data as you print the list. You'll see that both nodes have pointers to the same chunk of memory, the value you got back from that call to malloc.
But it would be much more elegant if your list entries owned their contents. That would require functions like push and append to allocate their own pointers, copy the strings into them, and use those new pointers. Your code to destroy a list could call free on the data pointed to as well as the nodes.
I would suggest a completely different approach.
I would use a C99 flexible array member for storing each word. Also, because I don't want my code to be submittable as a homework answer, I'll show how to do it with wide-character input. (On basically all OSes except possibly Windows, it treats non-ASCII characters like Ö and Ø as letters, if your locale says they are.)
struct word {
struct word *next;
wchar_t data[]; /* Flexible array member */
};
I would use a helper function that reads the next word from a wide stream, skipping any non-word characters (which I assume to be alphanumeric characters, i.e. letters and digits):
struct word *wide_word(FILE *input)
{
struct word *w = NULL, *tempw;
size_t max = 0; /* No characters allocated in w yet */
size_t len = 0; /* No characters in w yet */
wint_t c;
/* NULL input is not allowed. */
if (!input) {
errno = EINVAL;
return NULL;
}
/* Also fail if the stream is already in an error state. */
if (ferror(input)) {
errno = EIO;
return NULL;
}
c = getwc(input);
/* Skip leading non-word characters. */
while (c != WEOF && !iswalnum(c))
c = getwc(input);
/* End of input? */
if (c == WEOF) {
errno = 0;
return NULL;
}
/* Append each wide word character. */
while (c != WEOF && iswalnum(c)) {
/* Need to reallocate? */
if (len >= max) {
/* Since words are usually short, let's allocate
in chunks of 64 wide characters. */
max = (len | 63) + 65;
tempw = realloc(w, sizeof (struct word) + max * sizeof (wchar_t));
if (!tempw) {
/* Out of memory. */
free(w);
errno = ENOMEM;
return NULL;
}
w = tempw;
}
/* Append. */
w->data[len++] = c;
c = getwc(input);
}
/* Although not useful for this particular case,
we normally want to keep the separator intact. */
if (c != WEOF)
ungetwc(c, input);
/* Optimize the memory allocated to the word. */
if (max != len + 1) {
max = len + 1;
tempw = realloc(w, sizeof (struct word) + max * sizeof (wchar_t));
if (!tempw) {
free(w);
errno = ENOMEM;
return NULL;
}
w = tempw;
}
/* Terminate the wide string in w. */
w->data[len] = L'\0';
/* Success! */
return w;
}
I personally prefer to prepend new nodes to the list, then reverse the entire list afterwards:
struct word *reverse_list(struct word *oldlist)
{
struct word *newlist = NULL;
struct word *w;
while (oldlist) {
w = oldlist;
oldlist = oldlist->next;
w->next = newlist;
newlist = w;
}
return newlist;
}
With the above, a program to read wide words from standard input is basically
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <locale.h>
#include <stdio.h>
#include <wchar.h>
#include <errno.h>
/* Functions listed above */
int main(void)
{
struct word *list, *node;
if (!setlocale(LC_ALL, ""))
fprintf(stderr, "Warning: Your C library does not support your current locale.\n");
if (fwide(stdin, 1) < 1)
fprintf(stderr, "Warning: Your C library does not support wide standard input.\n");
if (fwide(stdout, 1) < 1)
fprintf(stderr, "Warning: Your C library does not support wide standard output.\n");
/* Read words from standard input into reversed list. */
while (1) {
node = wide_word(stdin);
if (!node) {
if (errno) {
fprintf(stderr, "Error reading standard input: %s.\n", strerror(errno));
exit(EXIT_FAILURE);
}
/* No error, just end of input. */
break;
}
/* Prepend to list. */
node->next = list;
list = node;
}
/* Reverse the list so first word is first in list. */
list = reverse_list(list);
/* Print each word in the list to standard output, in order. */
for (node = list; node != NULL; node = node->next)
wprintf(L"%ls\n", node->data);
/* We could free() each word in 'list' here. */
return EXIT_SUCCESS;
}

Why am I getting a segmentation fault on a limit less than 169? [closed]

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.

C - How to setup an associative array of function pointers?

Here's what I'm trying to do:
myValueType function1(myParam){}
myValueType function2(myParam){}
myArray[CONSTANT_STATE1] = &function1;
myArray[CONSTANT_STATE2] = &function2;
myValue = (*myArray[CONSTANT_STATE1])(myParam);
When I compile, it throws an error that I've redeclared function1.
What's the best way to do this?
As per this SO answer from user Vijay Mathew:
Section 6.6 of The C Programming Language presents a simple dictionary (hashtable) data structure. I don't think a useful dictionary implementation could get any simpler than this. For your convenience, I reproduce the code here.
struct nlist { /* table entry: */
struct nlist *next; /* next entry in chain */
char *name; /* defined name */
char *defn; /* replacement text */
};
#define HASHSIZE 101
static struct nlist *hashtab[HASHSIZE]; /* pointer table */
/* hash: form hash value for string s */
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 */
}
char *strdup(char *);
/* 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 = strdup(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 = strdup(defn)) == NULL)
return NULL;
return np;
}
char *strdup(char *s) /* make a duplicate of s */
{
char *p;
p = (char *) malloc(strlen(s)+1); /* +1 for ’\0’ */
if (p != NULL)
strcpy(p, s);
return p;
}
Note that if the hashes of two strings collide, it may lead to an O(n) lookup time. You can reduce the likely hood of collisions by increasing the value of HASHSIZE. For a complete discussion of the data structure, please consult the book.
The code you've shown is almost right. The problem is in your function declarations:
myValueType function1(myParam){}
myValueType function2(myParam){}
These are old-style K&R non-prototyped declarations - the name of the parameter is myParam, and the type has not been specified. Perhaps you meant this?
myValueType function1(myParamType myParam){}
myValueType function2(myParamType myParam){}
Expanding your code out to a minimal compilable example:
typedef int myValueType, myParamType;
enum { CONSTANT_STATE1, CONSTANT_STATE2 };
myValueType function1(myParamType myParam){}
myValueType function2(myParamType myParam){}
void f(myParamType myParam)
{
myValueType myValue;
myValueType (*myArray[2])(myParamType);
myArray[CONSTANT_STATE1] = &function1;
myArray[CONSTANT_STATE2] = &function2;
myValue = (*myArray[CONSTANT_STATE1])(myParam);
}

Fixing Memory Leaks in Chapter 6.5 Example in the K&R book (Self-Referential Structures)

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.

Character pointer assignment

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.

Resources