C - LinkedList Sorting Infinite Loop - c

I am creating a program that will read a word from a text file in main.c, and send it over to list.c file to create a new node to store that word. The node will also store three ints: first (number of times this word appears in txt file 1), second(number of times this word appears in txt file 2), and dif (abs(first-second)). After adding all the new words to the file and counting the number of times each word exists in each txt file, the main.c will call a method that will calculate the difference between first and second for each node. This is difference (stored in dif for each node) will be used to sort the linked nodes in decreasing order.
EX. word: the, first: 2888, second: 2466, dif: 422.
red, 39 12 27
.....
However, when main calls the sort method, a infinite loop occurs. This infinite loop comes from the inner loop of the sorting algorithm, where the current node is assigned the node from the curr->next pointer. Somewhere during the sort method, the current node's next pointer points to the current node, not the actual next node in the linkedlist. If the sort method is dactivated, then all other functions work fine, including printAll which goes through the entire list and prints the data in each node (see my example above).
My issue is that I cannot find where in my sort method how current->next started to point to the current node. Any help is appreciated!
/*
* list.h
*/
#ifndef LIST_H_
#define LIST_H_
typedef struct node Node;
void findWord(char *word, int book);
void addWord(char *word, int book);
void editWord(Node **endPtr, int book);
void sort();
void swap(Node **a, Node **b);
void calculateDiff();
void printAll();
#endif /* LIST_H_ */
/*
* list.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "list.h"
typedef struct node{
int first;
int second;
int dif;
char name[20];
struct node *next;
}Node;
Node *front = NULL;
/*
* Sees if the current word exists in the
* linkedlist.
*/
void findWord(char *word, int book) {
Node *curr = front;
int boolean = 0;
while (curr != NULL) {
if(strcmp(curr->name, word) == 0) {
boolean = 1;
editWord(&curr, book);
break;
}
curr = curr->next;
}
if(!boolean) { //Add word if it does not exist.
addWord(word, book);
}
}
/*
* Creates a new node for the added word. Adds to front.
*/
void addWord(char *word, int book) {
Node *newNode = malloc (sizeof(Node));
/*
* Since this word is being added
* to the linkedlist with a newly
* created node, either the
* first or second int must be to 1
* while the other is set to 0. Based
* off of book int.
*/
if(book == 1) {
newNode->first = 1;
newNode->second = 0;
} else {
newNode->first = 0;
newNode->second = 1;
}
newNode->dif = 0;
strcpy(newNode->name, word);
newNode->next = front;
front = newNode;
}
/*
* Edits the data for an existing word.
* Only called if current word exists in
* the linkedlist.
*/
void editWord(Node **endPtr, int book) {
if (book == 1) {
(*endPtr)->first++;
} else {
(*endPtr)->second++;
}
}
/*
* Sorts the list in descending order based on
* difference value.
*/
void sort() {
Node *curr, *last = NULL;
curr = front;
while (curr != last) {
while (curr->next != last) {
if(curr->dif < curr->next->dif ) {
swap(&curr, &curr->next);
}
curr = curr->next;
}
last = curr;
curr = front;
}
}
/*
* Swaps the data in the current and next node in the list.
*/
void swap(Node **a, Node **b) {
int temp;
char nameTemp[20];
//Swap first
temp = (*a)->first;
(*a)->first = (*b)->first;
(*b)->first = temp;
//Swap second
temp = (*a)->second;
(*a)->second = (*b)->second;
(*b)->second = temp;
//Swap dif
temp = (*a)->dif;
(*a)->dif = (*b)->dif;
(*b)->dif = temp;
//Swap name
strcpy(nameTemp, (*a)->name);
strcpy((*a)->name, (*b)->name);
strcpy((*b)->name, nameTemp);
}
/*
* Calculates the difference between first and second
*/
void calculateDiff() {
Node *curr = front;
while(curr != NULL) {
curr->dif = abs((curr->first - curr->second));
curr = curr->next;
}
}
/*
* Prints all the data from the nodes.
*/
void printAll() {
printf("|| Word || RedBadge || LittleRegiment || Diff\n");
Node *curr = front;
while ( curr != NULL ) {
printf("%s, %d, %d, %d\n", curr->name, curr->first, curr->second, curr->dif);
curr = curr->next;
}
}
/*
* main.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "list.h"
void readBook(int book, FILE *infile);
void readLine(char *line, int book);
int main (void) {
setvbuf(stdout, NULL, _IONBF,0);
FILE *infile = fopen("RedBadge.txt", "r");
FILE *infile2 = fopen("LittleRegiment.txt", "r");
readBook(1, infile);
readBook(2, infile2);
fclose(infile);
fclose(infile2);
calculateDiff();
sort();
printAll();
return 0;
}
void readBook(int book, FILE *infile) {
char line[70];
//Read in each line
while (!feof(infile)) {
fgets(line, 70, infile);
readLine(line, book);
}
}
void readLine(char *line, int book) {
int i = 0, j = 0;
char word[20];
while (line[i]) {
line[i] = tolower(line[i]); //Convert line to lowercase
if((line[i] <= 'z' && line[i] >= 'a') || line[i] == 39 || line[i] == '-') {
word[j] = line[i];
j++;
} else if (j != 0) {
word[j] = '\0';
findWord(word, book);
j = 0;
}
i++;
}
}

I believe your error is actually a buffer overflow. There are words in those books that are longer than 19 characters (the max that will fit in your word variable). When your readline function tries to read those words it will write outside the boundaries of the word array, which is undefined behavior. It will then also use strcpy to copy the word into the node, which will also overflow the node's word array.
A quick fix is to just throw away the extra characters past 19 that won't fit in your word array. In readline add a test for how big j is:
if (j < sizeof word - 1) {
word[j] = line[i];
j++;
}
One of the words in question is "ain't--plundering----" (at least in the copy of the text i downloaded), which leads me to think maybe you also should split words on punctuation.

Related

Creating a singly linked list from a .txt file and reversing odd numbers of each line in C

I have a project about linked lists but I'm having a hard time doing it. The teacher wants me to read a .txt file and create singly linked list from it. After that, I need to reverse odd numbers of every line. Then print it. Here is the code which I used for printing the linked list. But I need help to reverse the odd numbers of each line.
This is the code which I used to print the list:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct list {
char *string;
struct list *next;
};
typedef struct list LIST;
int main(void) {
FILE *fp;
char line[10];
LIST *current, *head;
head = current = NULL;
fp = fopen("data.txt", "r");
while(fgets(line, sizeof(line), fp)){
LIST *node = malloc(sizeof(LIST));
node->string = strdup(line);
node->next =NULL;
if(head == NULL){
current = head = node;
} else {
current = current->next = node;
}
}
fclose(fp);
for(current = head; current ; current=current->next){
printf("%s", current->string);
}
return 0;
}
Here is the content of the .txt file:
10
9,6,11,7,12,18,19,14,15,13
13,14,9,12,15,3,18,20,1,2
4,11,8,17,12,15,20,10,3,16
19,4,11,1,13,17,12,16,20,18
1,6,20,11,13,9,7,16,10,2
12,4,11,16,3,20,9,19,17,15
20,3,10,12,18,2,5,14,15,16
18,19,15,2,6,9,1,3,17,4
7,6,20,1,11,4,3,5,8,16
1,2,16,13,17,10,12,9,4,15
"But I need help to reverse the odd numbers of each line."
There are several other parts that need to be considered before this step can be developed.
Following are suggestions for a functions approach implementation using your problem description. A few items are simply suggestions to simplify the existing code. And a few other steps, are not mentioned as necessary, but should be considered:
Since you are not mandated to use char *string; in your problem description, choose to use a reasonable string length variable that does not require an additional layer of dynamic allocation, such as char string[260]; (or even smaller to fit your input file.) This will greatly simplify the code.
Because the input file is sized with lines ~30 char long, declare the variable line to be at least large enough to contain one line, eg 80 would allow larger values, and still allow enough space, but since memory is cheap, go with the same size as is used in the string member of your linked list.
Move the work of populating each new node to a function. It also will greatly simplify the program, and provide greater readability. Eg: void insert(LIST **head_ref, char *str);
Always test the return of fopen() before attempting to use the file descriptor.
To manipulate the contents of each odd row (eg 1, 3, 5, 7, 9), as numbers, the contents of each line read in from a file as a string, needs to first be converted to a collection of numbers. This suggests an additional member be added to the struct. For example int num[10].
The previous observation implicitly suggests the need of an additional function to parse and convert each comma delimited string into discrete integer values. Perhaps with the prototype: void parseIntArray(LIST **list);
The next and final task also suggests an additional function to reverse the contents of selected array member integer arrays. This one might use a prototype such as: void reverse_odd(LIST **list, size_t size);
Finally, because each node of LIST created required dynamically allocated memory, once finished using LIST, the memory must be given back to the OS to prevent memory leaks. An additional function to do this could be prototyped: void freeList(LIST **head);
Following are the main() function and preceding support declarations etc. It is intended here to illustrate the above suggested steps, and the benefits of breaking down a bigger problem into smaller problems, then implementing each smaller solution to support the whole. Benefits include for example readability and maintainability and potential re-use of code-base, (Note the similarity of argument lists in each supporting function.):
#define MAX_STRLEN 260 //use mnemonic values to avoid magic numbers in code
struct list {
char string[MAX_STRLEN];
int arr[10];
struct list *next;
};
typedef struct list LIST;
//Prototypes of 'smaller' solutions
void insert(LIST **head_ref, char *str);
void parseIntArray(LIST **list);
void reverse_odd(LIST **list, size_t size);
void freeList(LIST **head);
int main(void)
{
FILE *fp;
char line[MAX_STRLEN];
LIST *current, *head;
char *convPtr = NULL;
head = current = NULL;
fp = fopen("data.txt", "r");
if(fp)
{
//consume 1st line
if(fgets(line, sizeof(line), fp));//10
{
sizeArray = strtol(line, &convPtr, 10);
if(errno != ERANGE)
{
while(fgets(line, sizeof(line), fp))
{
//(see implementations of each below)
//create new node, insert num string
insert(&current, line);
//convert new->string to integers, place in new->array
parseIntArray(&current);
//reverse 'odd' contents of each array
reverse_odd(&current, sizeArray);
}
}else{//handle error and leave}
}
fclose(fp);
}else{//handle error and leave}
//At this point in code, entire file is captured into nodes of list.
//use list as needed
//When finished using list, memory must be freed to prevent memory leaks
head = current;
freeList(&head);
return 0;
}
The remaining code segments are the function implementations used above:
void freeList(LIST **head)
{
LIST *tmp;
while (*head != NULL)
{
tmp = (*head);
(*head) = (*head)->next;
free(tmp);
}
}
//create new node, insert num string
void insert(LIST **head_ref, char *str)
{
int *arr = malloc(numNodes * sizeof(*arr));
//allocate node
LIST* new = calloc(1, sizeof(*new));
//put in the data
strcpy(new->string, str);
//Make next of new node as head
new->next = (*head_ref);
//Move the head to point to the new node
(*head_ref) = new;
}
//convert new->string to integers, place in list->array
void parseIntArray(LIST **list)
{
char *tok = NULL;
int i = 0;
int tmp = 0;
char *sArray = strdup((*list)->string);
tok = strtok(sArray, ",\n ");
while(tok)
{
errno = 0;
tmp = atoi(tok);
if(errno == ERANGE)
{
printf("Error converting string to number\nExiting.");
return;
}
(*list)->arr[i] = tmp;
i++;
tok = strtok(NULL, ",\n ");
}
}
//reverse 'odd' contents of list->array
void reverse_odd(LIST **list, size_t size)
{
int *ptr = &((*list)->arr[0]);
int *tmp = malloc(size * sizeof(*tmp));
memset(tmp, -1, size*sizeof(*tmp));
for(int i=0;i<size;i++)
{
if(ptr[i]%2 != 0)
tmp[size-1-i] = ptr[i];
}
for(int i=0;i<size;i++)
{
if(tmp[i] < 0)
{
while((*ptr)%2 != 0 ) ptr++;
tmp[i] = *ptr;
ptr++;
}
}
memcpy((*list)->arr, tmp, size*sizeof(int));
}
This hope this code will do the job.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct line {
struct num *first;
struct line *next;
} LineNode;
typedef struct num {
int num;
int order;
struct num *next;
} NumNode;
int main() {
FILE *fp;
char ch;
int counter = 0;
NumNode *curr_num, *even_ptr, *odd_ptr, *odd_head, *even_head;
LineNode *curr_line, *line_head;
curr_num = even_head = odd_head = even_ptr = odd_ptr = NULL;
line_head = curr_line = NULL;
fp = fopen("data.txt", "r");
if (fp == NULL)
{
return 1;
}
ch = fgetc(fp);
while(ch != EOF){
if (ch >= 48 && ch <= 57)
{
int n = 0;
while (ch != EOF && ch != '\n' && ch >= 48 && ch <= 57)
{
int x = ch - 48;
n = n * 10 + x;
ch = fgetc(fp);
}
NumNode *node = malloc(sizeof(NumNode));
node->num = n;
node->order = counter;
node->next =NULL;
if (n % 2 == 0){
if(even_head == NULL){
even_head = even_ptr = node;
} else {
even_ptr = even_ptr->next = node;
}
}else{
if(odd_head == NULL){
odd_head = node;
} else {
node->next = odd_head;
odd_head = node;
}
}
counter++;
}
if (ch == '\n' || ch == EOF)
{
NumNode *num_node, *head;
num_node = head = NULL;
even_ptr = even_head;
odd_ptr = odd_head;
counter = 0;
if (even_head != NULL && even_head->order == counter){
head = num_node = even_ptr;
even_ptr = even_ptr->next;
} else {
head = num_node = odd_ptr;
odd_ptr = odd_ptr->next;
}
counter++;
while (even_ptr != NULL)
{
if (even_ptr->order == counter) {
num_node = num_node->next = even_ptr;
even_ptr = even_ptr->next;
}
else if (odd_ptr != NULL) {
num_node = num_node->next = odd_ptr;
odd_ptr = odd_ptr->next;
}
counter++;
}
while (odd_ptr != NULL)
{
num_node = num_node->next = odd_ptr;
odd_ptr = odd_ptr->next;
}
LineNode *node = malloc(sizeof(LineNode));
node->next =NULL;
node->first = head;
if (line_head == NULL)
line_head = curr_line = node;
else
curr_line = curr_line->next = node;
odd_head = even_head = NULL;
counter = 0;
}
ch = fgetc(fp);
}
fclose(fp);
for(curr_line = line_head; curr_line != NULL ; curr_line=curr_line->next) {
for(curr_num = curr_line->first; curr_num != NULL ; curr_num=curr_num->next) {
printf("%d", curr_num->num);
if (curr_num->next != NULL)
printf(",");
}
printf("\n");
}
return 0;
}

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;
}

C - sorted linked list with words and frequencies

I'm having some issues finalizing my code for my programming course (I'm an absolute beginner in C). The aim is to read words from standard input (runfile < input.c), count their frequencies, and sort the list alphabetically (capitalized words first), example output:
Image Sample output
I found pieces of codes here on Stack, which I adapted, and so far it produces the output with words and their frequencies. However, I can't figure out how to get the list sorted as in the sample above. Our teacher suggests, that if a new word is found, it should be inserted sorted straight away into the linked list, he gave us following code sample (it is an excerpt from this program):
void addSorted(link *n, int x) {
if (*n == NULL || x < (*n)->data) {
*n = cons(x, *n);
} else {
addSorted(&((*n)->next), x);
}
}
As far as I understand it, 'link *n' should be the pointer to the next node, 'data' is holding integers in that case, and 'cons' should be a function within this code to construct a new node or link, not sure about 'int x', my guess it's the current integer for comparison.
As I said, I'm having trouble adapting this last bit into my code. I tried to adapt my addWord() function, but it doesn't work out for me.
Below you find the working code I have so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
//=============== STRUCTURE ==================
typedef struct word {
char *mywords; // list node with word pointer
int freq; // Frequency count
struct word *pNext; // Pointer to next node in linked list
} Word;
//======= INITIATION OF FUNCTIONS ===========
int readWord(char *temp_word, int temp_size); // Given function to get words
void addWord(char *pWord); // Adds a word to the list or updates exisiting word
void printmywords(Word *pListNodes); // Output list of words and frequencies
Word* construct(char *word); // Constructs list nodes
//============GLOBAL VARIABLES================
Word *pFirst = NULL; // Pointer to first node in linked list
//================ MAIN ======================
int main () {
char temp_word[32]; // temporary buffer to hold words
int size = 10000;
Word *pNode = NULL; // pointer to word counter
while (readWord(temp_word, size)) { // Read all words from standard input
addWord(temp_word); // Add word to list
}
// List the words and their counts
pNode = pFirst;
while(pNode != NULL)
{
printmywords(pNode);
pNode = pNode->pNext;
}
printf("\n");
// Free the allocated memory
pNode = pFirst;
while(pNode != NULL)
{
free(pNode->mywords);
pFirst = pNode;
pNode = pNode->pNext;
free(pFirst);
}
return 0;
}
//================ FUNCTIONS =================
void printmywords(Word *pListNodes)
{
printf("\n%-20s %5d", pListNodes->mywords,pListNodes->freq); // output word and frequency
}
void addWord(char *word)
{
Word *pNode = NULL;
Word *pLast = NULL;
if(pFirst == NULL)
{
pFirst = construct(word);
return;
}
// Update frequency, if word in list
pNode = pFirst;
while(pNode != NULL)
{
if(strcmp(word, pNode->mywords) == 0)
{
++pNode->freq;
return;
}
pLast = pNode;
pNode = pNode->pNext;
}
// Add new word, if not in list
pLast->pNext = construct(word);
}
Word* construct(char *word)
{
Word *pNode = NULL;
pNode = (Word*)malloc(sizeof(Word));
pNode->mywords = (char*)malloc(strlen(word)+1);
strcpy(pNode->mywords, word);
pNode->freq = 1;
pNode->pNext = NULL;
return pNode;
}
int readWord(char *temp_word, int temp_size) {
char *p = temp_word;
char c;
// skip all non-word characters
do {
c = getchar();
if (c == EOF)
return 0;
} while (!isalpha(c));
// read word chars
do {
if (p - temp_word < temp_size - 1)
*p++ = c;
c = getchar();
} while (isalpha(c));
// finalize word
*p = '\0';
return 1;
}
Any help is appreciated.
Okay, try these two functions:
Word *cons(char *word, Word *next) {
Word *result = construct(word);
if (result) {
result->pNext = next;
}
else {
printf("Out of memory in cons\n");
exit(1);
}
return result;
}
void addSorted(Word **nodeRef, char *word) {
Word *node = *nodeRef;
/* strcmp will do a binary comparison, which suits your purpose
because you want capitalized words before lower-case; the order
of the arguments is important - <0 means the first argument should
come before the second argument. */
if ((node == NULL) || (strcmp(word, node->mywords) < 0)) {
*nodeRef = cons(word, node);
}
else if (strcmp(word, node->mywords) == 0) {
++node->freq;
}
else {
/* there's not really any point to using recursion on a linked
list, except for the fact that it's really easy to use recursion
on a linked list. On a vary large list, iteration would most likely
be faster; however, professors really like to show how clever they
are, so you're better off using it anyway. */
addSorted(&node->pNext, word);
}
}
A couple of other points:
char temp_word[32]; // temporary buffer to hold words
int size = 10000;
You've got a 31 character buffer, but you're telling your readWord function that it is 10K characters?
Also, don't cast the return value from malloc().

Reading file into linked list

I am trying to read a text file I made into a linked list, the text file looks like this:
around 1 2 1
bread 2 4 3 5 1
four 1 3 2
head 3 1 2 2 1 5 1
has 2 3 1 5 2
Where the first string of each line are just words from a paragraph. The first number after the word is the number of lines the word was found in, in the paragraph. Then the following numbers are pairs of (line, occurrences) in the paragraph.
For example, for the word bread:
It was found in 2 lines in the paragraph. In the first line, line 4, it was found 3 times. Then in the second line, line 5, it was found 1 time.
I am trying to create a linked list from this text file, my program looks like this so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#define MAXWORD 999
typedef struct node node_t;
struct node {
char *word;
int num_lines;
int paragraph;
int freq;
node_t *next;
};
int
main(int argc, char *argv[]) {
FILE *fp;
char word[MAXWORD+1];
int ch, line_count = 0, len = 0;
node_t *node = (node_t*)malloc(sizeof(*node));
node_t *curr, *prev;
fp = fopen(argv[1], "r");
if (fp == NULL) {
fprintf(stderr, "Error reading file\n");
exit(EXIT_FAILURE);
}
/* Just trying to store the string so far */
while ((ch = getc(fp)) != EOF) {
if (ch == '\n') {
line_count++;
strcpy(node->word, word);
}
if (isalpha(ch)) {
word[len] = ch;
len++;
word[len] = '\0';
}
if (isdigit(ch)) {
len = 0;
}
}
printf("line count = %d", line_count);
free(node)
fclose(fp);
return 0;
}
In this snippet, I have been trying to store the string in the linked list data structure, but I have not yet used dynamic arrays to store the numbers after the word which occur in the text file. I know I will need to build this data structure using malloc() and realloc(), but I am unsure of how to do this.
How should I do this?
My desired output would look like this:
There are five words in the text file,
and 9 pairs of (line, occurences)
Word: pairs
"around": 2,1
"bread": 4,3; 5,1
"four": 3,2
"head": 1,2; 2,1; 5,1
"has": 3,1; 5,2
UPDATE
I have been researching this and it seems to be very similar to the inverted index problem, where I have seen that using a binary search tree would be best.
Could I implement my binary search tree like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#define MAXWORD 999
typedef char word_t[MAXWORD+1];
typedef struct node node_t;
struct node {
void *data;
int *ints;
node_t *rght;
node_t *left;
};
typedef struct {
node_t *root;
int (*cmp)(void*, void*);
} tree_t;
int
main(int argc, char *argv[]) {
FILE *fp;
fp = fopen(argv[1], "r");
if (fp == NULL) {
fprintf(stderr, "Error reading file\n");
exit(EXIT_FAILURE);
}
while ((ch = getc(fp)) != EOF) {
if (ch == '\n') {
line_count++;
}
}
fclose(fp);
return 0;
}
You could do something like this:
typedef struct {
int paragraph;
int freq;
} stats_t;
struct node {
char *word;
int num_lines;
stats_t *stats;
node_t *next;
};
Then after you parse the string you can do:
ps = calloc(line_count, sizeof(stats_t));
to get a pointer to an array of stats_t structs, which you can fill with line locations and frequencies. Then you can store the pointer ps in your node struct.
I wrote a program that does what I think you are looking for. I modified the structs I was thinking about before:
typedef node node_t;
struct node {
char *word;
int num_lines;
int *location;
int *frequency;
node_t *next;
};
This way the nodes contain pointers to arrays of int to store the location and frequency information. Nodes and storage for the word strings, location arrays, and frequency arrays are all dynamically allocated. Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAXLINE 1000
#define MAXWORD 30
typedef struct node node_t;
struct node {
char *word;
int num_lines;
int *location;
int *frequency;
node_t *next;
};
void strip(char *pln);
void normalize_word(char *pstr);
struct node * update_word(char *pwd, int lnum, struct node *phead);
struct node * find_in_list(char *pwd, struct node *phead);
int find_line_pair(int lnum, struct node *pwn);
int list_len(struct node *phead);
int num_pairs(struct node *phead);
int main(int argc, char *argv[])
{
FILE *fp;
struct node *head, *current;
char *pline, *pword;
char line[MAXLINE + 1];
char word[MAXWORD + 1];
int i, n, line_count = 0;
head = NULL;
if (argc < 2) {
fprintf(stderr, "Usage: %s filename\n", argv[0]);
exit(EXIT_FAILURE);
} else {
if ((fp = fopen(argv[1], "r")) == NULL) {
fprintf(stderr, "Unable to open file %s\n", argv[1]);
exit(EXIT_FAILURE);
}
}
/* Read in lines and process words */
pline = line;
pword = word;
while (fgets(pline, MAXLINE, fp) != NULL) {
++line_count;
strip(pline);
while ((pword = strtok(pline, " ")) != NULL) {
normalize_word(pword);
if (*pword != '\0') // don't add empty words
head = update_word(pword, line_count, head);
pline = NULL;
}
pline = line;
}
/* Display list contents */
printf("There are %d words in the text file,\n",
list_len(head));
printf("and %d pairs of (line, occurrences)\n",
num_pairs(head));
printf("Word: pairs\n");
current = head;
while (current != NULL) {
n = current->num_lines;
printf("%s:", current->word);
for (i = 0; i < n; i++) {
printf(" %d, %d;",
current->location[i], current->frequency[i]);
}
putchar('\n');
current = current->next;
}
/* Cleanup */
// close file
if (fclose(fp) != 0)
fprintf(stderr, "Error closing file %s\n", argv[1]);
// free all allocated memory
current = head;
while (current != NULL) {
free(current->word);
free(current->location);
free(current->frequency);
current = current->next;
free(head);
head = current;
}
return 0;
}
/* Remove trailing newlines */
void strip(char *pln)
{
while (*pln != '\0') {
if (*pln == '\n')
*pln = '\0';
++pln;
}
}
/* Convert word to lowercase and remove trailing
* non-alphanumeric characters */
void normalize_word(char *pstr)
{
int i = 0;
char ch;
while ((ch = pstr[i]) != '\0') {
pstr[i] = tolower(ch);
++i;
}
while ((--i >= 0) && !isalnum(pstr[i])) {
pstr[i] = '\0';
continue;
}
}
/* Update existing word node or create a new one, and return
* a pointer to the head of the list */
struct node * update_word(char *pwd, int lnum, struct node *phead)
{
struct node *found, *newnode;
char *pword;
int *ploc, *pfreq;
int index;
/* Modify existing node if word is in list */
if ((found = find_in_list(pwd, phead)) != NULL) {
// add new (location, freq) pair if word not in found line
if ((index = find_line_pair(lnum, found)) == -1) {
index = found->num_lines; // index for new pair
found->num_lines += 1; // increment number of lines
ploc = realloc(found->location, (index + 1) * sizeof(int));
pfreq = realloc(found->frequency, (index + 1) * sizeof(int));
ploc[index] = lnum; // new location
pfreq[index] = 1; // found once in this line so far
found->location = ploc; // point to new location array
found->frequency = pfreq; // point to new frequency array
}
else { // update frequency in existing line
found->frequency[index] += 1;
}
/* Set up a new node */
} else {
// allocate memory for new node
newnode = malloc(sizeof(struct node));
// allocate memory for string pointed to from node
pword = malloc((strlen (pwd) + 1) * sizeof(char));
strcpy(pword, pwd);
newnode->word = pword; // set word pointer
newnode->num_lines = 1; // only one line so far
ploc = malloc(sizeof(int));
pfreq = malloc(sizeof(int));
*ploc = lnum; // location was passed by caller
*pfreq = 1; // only one occurrence so far
newnode->location = ploc;
newnode->frequency = pfreq;
if (phead == NULL) { // if wordlist is empty
newnode->next = NULL; // only/last link in the list
phead = newnode; // newnode is the head
} else {
newnode->next = phead; // insert newnode at front of list
phead = newnode;
}
}
return phead;
}
/* Return pointer to node containing word, or NULL */
struct node * find_in_list(char *pwd, struct node *phead)
{
struct node *current = phead;
while (current != NULL) {
if (strcmp(current->word, pwd) == 0)
return current; // word already in list
current = current->next;
}
return NULL; // word not found
}
/* Return index of existing line location, or -1 */
int find_line_pair(int lnum, struct node *pwn)
{
int n = pwn->num_lines;
int index = 0;
while (index < n) {
if (pwn->location[index] == lnum)
return index; // word already found in this line
++index;
}
return -1; // word not yet found in this line
}
/* Find number of nodes in linked list */
int list_len(struct node *phead)
{
int length = 0;
struct node *current = phead;
while (current != NULL) {
++length;
current = current->next;
}
return length;
}
/* Find number of (line, occurrence) pairs */
int num_pairs(struct node *phead)
{
int num = 0;
struct node *current = phead;
while (current != NULL) {
num += current->num_lines;
current = current->next;
}
return num;
}
Note: I modified this from the previous version in the update_word() function. The original code inserted a new node at the end of the list, so the resulting list contained words in order of their first appearance in the input text. This version inserts a new node at the beginning of the list, so the resulting list contains words in reverse order of their first appearance. This speeds up node insertion and simplifies the node-insertion code from:
current = phead;
while (current->next != NULL) // find tail
current = current->next;
current->next = newnode; // add newnode to end
to:
newnode->next = phead; // insert newnode at front of list
I have no doubt that the code can be improved, but this does seem to work. I wouldn't say that this is exactly simple, but relatively straightforward. I ran it against this text file:
Three blind mice. Three blind mice.
See how they run. See how they run.
They all ran after the farmer's wife,
Who cut off their tails with a carving knife,
Did you ever see such a sight in your life,
As three blind mice?
Here are the results:
There are 31 words in the text file,
and 37 pairs of (line, occurrences)
Word: pairs
as: 6, 1;
life: 5, 1;
your: 5, 1;
in: 5, 1;
sight: 5, 1;
such: 5, 1;
ever: 5, 1;
you: 5, 1;
did: 5, 1;
knife: 4, 1;
carving: 4, 1;
a: 4, 1; 5, 1;
with: 4, 1;
tails: 4, 1;
their: 4, 1;
off: 4, 1;
cut: 4, 1;
who: 4, 1;
wife: 3, 1;
farmer's: 3, 1;
the: 3, 1;
after: 3, 1;
ran: 3, 1;
all: 3, 1;
run: 2, 2;
they: 2, 2; 3, 1;
how: 2, 2;
see: 2, 2; 5, 1;
mice: 1, 2; 6, 1;
blind: 1, 2; 6, 1;
three: 1, 2; 6, 1;
Here is my version using Binary Search Tree (BST):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
typedef struct internal_node in_node;
struct internal_node{
int line;
int freq;
in_node* next;
};
struct tree{
char *word;
int num_lines;
in_node* in_nodeptr;
in_node* current;
struct tree* right;
struct tree* left;
};
typedef struct tree* treeptr;
void free_list(in_node* in_nodeptr){
if(in_nodeptr!=NULL) {
free(in_nodeptr);
}
}
void free_bst(treeptr head){
if (head!=NULL) {
free_bst(head->right);
free_bst(head->left);
free_list(head->in_nodeptr);
free(head->word);
free(head);
}
}
void print_list(in_node* in_nodeptr){
while(in_nodeptr!=NULL){
printf("%d %d; ",in_nodeptr->line,in_nodeptr->freq);
in_nodeptr=in_nodeptr->next;
}
}
void print_bst(treeptr head){
if(head!=NULL){
printf("%s: ",head->word);
print_list(head->in_nodeptr);
printf("\n");
print_bst(head->right);
print_bst(head->left);
}
}
void input_to_bst(treeptr* head,char* word,int line){
if((*head)==NULL){
(*head)=(treeptr)malloc(sizeof(struct tree));
(*head)->word=(char*)malloc(50*sizeof(char));
strcpy(((*head)->word),word);
(*head)->num_lines=1;
(*head)->right=NULL;
(*head)->left=NULL;
(*head)->in_nodeptr=(in_node*)malloc(sizeof(in_node));
(*head)->in_nodeptr->line=line;
(*head)->in_nodeptr->freq=1;
(*head)->in_nodeptr->next=NULL;
(*head)->current=(*head)->in_nodeptr;
}
else{
int check=strcmp(((*head)->word),word);
if(check>0) input_to_bst(&((*head)->left),word,line);
else if(check<0) input_to_bst(&((*head)->right),word,line);
else{
if( (*head)->current->line==line) (*head)->current->freq++;
else {
(*head)->current->next=(in_node*)malloc(sizeof(in_node));
(*head)->current->next->line=line;
(*head)->current->next->freq=1;
(*head)->current->next->next=NULL;
}
}
}
}
int main(int argc, char *argv[]) {
treeptr head=NULL;
FILE *fp=fopen(argv[1], "r");
char word[50],ch;
int len=0,lines=1;
if (fp == NULL) {
fprintf(stderr, "Error reading file\n");
exit(1);
}
while ((ch = getc(fp)) != EOF) {
if (ch == '\n') {
word[len]='\0';
if(len>0) input_to_bst(&head,word,lines);
len=0;
lines++;
}
else if (ch==' '){
word[len]='\0';
if(len>0) input_to_bst(&head,word,lines);
len=0;
}
else if (isalpha(ch)){
word[len]=ch;
len++;
}
}
if(len>0) {
word[len]='\0';
input_to_bst(&head,word,lines);
}
print_bst(head);
fclose(fp);
free_bst(head);
return 0;
}
Every word is held as a node of the BST and also each node of BST except from the word, holds a list with all the appearances (lines and frequency ) of the word. In order to be as most efficient as possible we hold a pointer (in_node* current) to the last element of list of appearance so that we don't need to traverses every time we need to add an appearance.
As an example:
Text:
C is an imperative procedural language. It was designed to be compiled
using a relatively straightforward compiler and to require minimal
runtime support.
Output:
C: 1 1;
is: 1 1;
procedural: 1 1;
was: 1 1;
to: 1 1; 2 1;
using: 2 1;
relatively: 2 1;
straightforward: 2 1;
support: 3 1;
require: 2 1;
runtime: 3 1;
language: 1 1;
minimal: 2 1;
an: 1 1;
imperative: 1 1;
designed: 1 1;
be: 1 1;
compiled: 1 1;
compiler: 2 1;
and: 2 1;
It: 1 1;
a: 2 1;
Note that the above implementation is case sensitive for example "And" is different from "and".
If you don't wish to be case sensitive just replace the line word[len]=ch; with word[len]=tolower(ch); and works fine.
The complexity of the above algorithm is O(n^2) which would be the same if you used only linked lists but in the average case BST is O(nlogn) which is much better than linked lists and this is the reason that it is considered to be the better.
Also note that since we must keep a list for appearances of each word the complexity would be worst if we didn't keep the in_node* current pointer which gives us access to the end of each appearance list in constant time (O(1)). So I think that as terms of complexity you can't go better than O(nlogn).

How would checking if a value in a data structure equals NULL return a segmentation fault?

I am trying to load a hash table of node*(s)-
typedef struct node{
char word[LENGTH+1];
struct node* next;
}node;
(don't worry about length, it is defined in the file that calls this)
-into memory, but this:
// make hash table
node* hashtable[729];
node* new_node = malloc(sizeof(node));
node* cursor = NULL;
int bucket;
while(sscanf(dictionary,"%s",new_node->word) != 0)
{
bucket = hash(new_node->word);
cursor = hashtable[bucket];
while(cursor->next != NULL)
{
cursor = cursor->next;
}
cursor->next = new_node;
}
return true;
keeps turning up to be a segmentation fault (specifically the condition of the while loop). I am baffled, what is going on? Thank you in advance to any who helps! I really appreciate your help!
You need to allocate memory for each node that is going into your hash table. How's about something like the following:
/* make hash table */
node* hashtable[729];
/* initialise all buckets to NULL */
memset(hashtable, 0, sizeof(node*)*729);
node new_node; /* Use a stack node for the temporary */
new_node.next = NULL;
node** cursor = NULL;
int bucket;
while(sscanf(dictionary,"%s",new_node.word) != 0)
{
bucket = hash(new_node.word);
cursor = &hashtable[bucket];
while(*cursor != NULL)
{
cursor = &(*cursor)->next;
}
if ((*cursor = malloc(sizeof(node))) != NULL)
/* Copy from temporary to hashed node. Assumes structure is 'flat' */
**cursor = new_node;
else {
/* panic! */
}
}
return true;
Edit:
I've refactored some code and produced a standalone example that compiles and runs, For simplicity, I've employed a totally bogus hash function and reduced the number of buckets to fit its output of 0-25. I've tried to split out the hashtable 'object' and started the effort to be a little more disciplined to avoid buffer overruns, etc.
For the traversal of the linked list of nodes in a bucket of the hashtable, I've included two versions--one that uses the node** (pointer to a pointer) and another that doesn't--in an attempt to demonstrate the use of the double star. Change the #if 1 to #if 0 to use the "single star" version.
I hope that, collectively, these changes help clarify (more than they obscure) the original purpose, although I apologise for the verboseness of the code that follows:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define LENGTH 64
typedef struct node {
char word[LENGTH+1];
struct node * next;
} node;
typedef struct hashtable {
node * table[26];
} hashtable;
/* The cleverest 'hashing' function in the world ever! */
int hash(const char * str) {
if (str && str[0]) return tolower(str[0]) - 'a';
return 0;
}
/* Allocate a new node and initialise it with the given word */
node * node_create(const char * word) {
node * nd = NULL;
if (word && (nd = malloc(sizeof(node)))) {
strncpy(nd->word, word, sizeof(nd->word)-1);
nd->word[sizeof(nd->word) - 1] = '\0';
nd->next = NULL;
}
return nd;
}
/* Set all the buckets' pointers to NULL */
void hashtable_init(hashtable * ht) {
if (ht) memset(ht, 0, sizeof(hashtable));
}
/* Place the given node into the hashtable, taking responsibility for it */
void hashtable_insert_node(hashtable * ht, node * nd) {
if (ht && nd) {
#if 1 /* More succint version using node** */
/* Identify the bucket this node should go into */
node ** cursor = ht->table + hash(nd->word);
/* Append this node to this bucket's list of nodes */
while (*cursor != NULL) cursor = &(*cursor)->next;
*cursor = nd;
#else /* Version that avoids use of node** */
int bucket = hash(nd->word);
/* Identify the bucket this node should go into */
if (ht->table[bucket]) {
node * cursor = ht->table[bucket];
while (cursor->next) cursor = cursor->next;
cursor->next = nd;
} else {
ht->table[bucket] = nd;
}
#endif
nd->next = NULL; // Ensure the new node is the last in the list
}
}
/* Free the contents of the given hashtable */
void hashtable_free_contents(hashtable * ht) {
if (ht) {
int i;
for (i=0; i < sizeof(ht->table)/sizeof(ht->table[0]); ++i) {
node * cursor = ht->table[i];
while (cursor != NULL) {
node * next = cursor->next;
free(cursor);
cursor = next;
}
}
}
}
/* Dump the contents of the given hashtable to stdout */
void hashtable_dump(const hashtable * ht) {
if (ht) {
int i;
for (i=0; i < sizeof(ht->table)/sizeof(ht->table[0]); ++i) {
printf("Bucket %d:", i);
node * cursor = ht->table[i];
while (cursor != NULL) {
printf(" %s", cursor->word);
cursor = cursor->next;
}
printf("\n");
}
}
}
int main(int argc, char * argv[]) {
char dictionary[] = {
"apples "
"apricots "
"oranges "
"lemons "
"bananas "
"raspberries "
"carrots "
"tomatoes "
"aubergines "
"limes "
"blueberries "
"plums "
"pears "
"peaches "
"pineapples "
"tangerines "
"kiwis "
"passion_fruit "
"strawberries "
};
hashtable ht;
hashtable_init(&ht);
char * p = dictionary; /* Pointer for traversing the dictionary */
node new_node; /* Temporary node for storing word read from dictionary */
new_node.next = NULL;
int n; /* Number of bytes read from dictionary in sscanf call */
char format[16];
/* If a huge word is in the dictionary, guard against buffer overflow */
snprintf(format, sizeof(format), "%%%ds%%n", sizeof(new_node.word));
while(sscanf(p, format, new_node.word, &n) == 1) {
/* Insert (a copy of the) new node into hashtable */
hashtable_insert_node(&ht, node_create(new_node.word));
/* Move forwards through the dictionary */
p += n;
}
hashtable_dump(&ht);
hashtable_free_contents(&ht);
return 0;
}
Just allocate memory for each node of the hashtable and then dereference them.
i.e.
int i ;
for(i = 0; i < 729; ++i) {
hashtable[i] = malloc(sizeof(node));
hashtable[i]->next = NULL ;
}

Resources