Disclaimer: This is a homework problem. As should be evident, I am trying to solve it myself. I seem to have hit a problem I am unable to figure out, so some help would be appreciated.
I am required to hash a set of words, and insert it into an array of linked lists. So, if 3 different words have the hash 38, at array[38], I need to have a linked list with the 3 words.
I am using this struct
struct Word {
char* word;
struct Word* next;
};
After I hash, I insert it into the array like this:
struct Word* table[1000]; // array of linked-lists
char word[128];
while(fgets(word, sizeof(word), dict) != NULL) {
hashValue = hashWord(word, strlen(word));
struct Word *newWord = malloc(sizeof(struct Word));
newWord->word = word;
if (table[hashValue] == NULL) {
table[hashValue] = newWord;
} else { // at index hashValue, table already contains at least one element
// insert new word as first element of linked-list
newWord->next = table[hashValue];
table[hashValue] = newWord;
}
}
I know there are about 5 words that have a hash of 38, but when I print them, I get the same word 5 times:
struct Word* foo = table[38];
while (foo->next != NULL) {
printf("%s", foo->word); // prints the same word every time
foo = foo->next;
}
It seems I am overwriting my linked list at some point, but I cannot figure out where.
while(fgets(word, sizeof(word), dict) != NULL) {
...
newWord->word = word;
The problem is that you're replacing the contents of word, which is the same pointer stored in every newWord.
You're allocating a new Word structure on the heap each time with this line:
struct Word *newWord = malloc(sizeof(struct Word));
But this only allocates memory for the Word structure itself; the Word structure includes a char *word—i.e. a pointer to a character (or NUL-terminated string, in this case), but it doesn't actually include any space for the string itself.
You need to explicitly allocate memory for the string, now. Try this:
newWord->word = strdup(word);
This will put a copy of word into each Word structure. This copy won't be overwritten when fgets() next gets called in your while loop. Remember that the stack-allocated character array:
char word[128];
is only valid while this function is executing; you must allocate something on the heap (with malloc(), or something that uses it, like strdup()) if you want it to live beyond the function call.
When you're finished, don't forget to free() the char *words before freeing each Word*.
You're overwriting the word array. You are only storing pointers to the array, but the array gets overwritten with each new word. You need separate memory to hold each word.
Related
I am having problem storing all the values into the Generic LinkedList, my linkedlist works totally works on a normal user Keyboard input but when I try to store values(strings) from a file, there is something weird happening, it only store the last value of the file.
I have checked my addToList() function but theres nothing wrong with it.
P.s But I am feeling its either I am printing wrong or my reading from the file into the linkedlist is wrong.
Thank you.
#include<stdio.h>
#include <stdlib.h>
#include<string.h>
#include "LinkedListItems.h"
#define MAX 10000
int main()
{
printf("Testing MissileFIle.txt");
void* secondStr;
//Had to malloc the thing
secondStr = (void*)malloc(1*sizeof(char));
FILE* missileFile;
missileFile = fopen("missiles.txt", "r");
if(missileFile == NULL)
{
printf("The file is empty");
}
number_list_t* missileList = calloc(1, sizeof(number_list_t));
void* input;
//Have to allocate the input
input = malloc(1*sizeof(void*));
//this is to read the data into the second Str
while(fgets(secondStr,MAX,missileFile) != NULL)
{
//Let just print out first just to test my memory
printf("%s\n",secondStr);
//Right now its only reading one string so far which is really weird AFFFFF
addTolist(missileList,secondStr);
}
//Gotta declare another list just to print out the list
number_node_t* current = missileList->head;
while(current != NULL)
{
//There is something wrong with this line
printf("%s\n",current-> number);
current = current-> next;
}
fclose(missileFile);
}
OUTPUT:
Testing MissileFile.txt
splash
single
V-line
h-line
Single
Single
Single
Single
Single
Single
typedef struct NumberNode
{
//It can store any data type
void* number;
struct NumberNode* next;
}number_node_t;
//List of Nodes
typedef struct NumberList
{
number_node_t* head;
int count; //This is not nesssary but it can be useful for counting how many variables
}number_list_t;
void addTolist(number_list_t* list, void* newNumber)
{
//tem[ = newNode]
number_node_t* newNode = calloc(1,sizeof(number_node_t));
newNode->number = newNumber;
newNode->next = list->head;
list->head = newNode;
}
INPUT DATA:
single
splash
single
V-Line
h-line
Single
The way you have implemented this, it cannot work.
The main problem, among many, is related to the void* pointers which cannot be dereferenced.
The size of elements should be given, either on creating the list in which case all elements are of the same type, or separately for each individual element. You can check out this question for an example of something that could work.
As far as the buffer thing is concerned, addToList should allocate new memory for each newNumber. What you are currently doing results in all data of the list pointing to a specific space in memory (the one allocated to secondStr). Each time you change the content of that memory space, all elements in the list are affected. This is why you print the same value for all elements and more specifically the last value in your file.
The way you allocate memory is also not really ok, same goes for the way you open your file, there is memory leaking etc. I am not going into details.
At least this issue:
Copy the string
OP's goal includes the need to copy the string from the read buffer to the list, not just copy the buffer pointer.
// void addTolist(number_list_t* list, void* newNumber) {
void addStringTolist(number_list_t* list, const char *s) {
// number_node_t* newNode = calloc(1,sizeof(number_node_t));
number_node_t* newNode = calloc(1, sizeof *newNode); // todo: add error check
size_t sz = strlen(s) + 1;
newNode->number = malloc(sz); // todo: add error check
strpy(newNode->number, s);
newNode->next = list->head;
list->head = newNode;
}
Note: When freeing the list, newNode->number also needs to be free'd.
regarding:
while(fgets(secondStr,MAX,missileFile) != NULL)
MAX is defined as 10000 but secondStr is defined as pointer to one byte. so when this is executed, a buffer overflow occurs.
This is undefined behavior and probably the root of the problem with reading from a file
I still have troubles with the relations between the linked lists and the structures.
See, my objectif is to create a list where each node contains 2 characters strings. So, I tried something like this : first, I create a structure that represent an element with my 2 char ; second, a control structure for my list, thath will point at the beginning of my list. Which, in my .h, gives something like this :
typedef struct s_def { char *first_word; char *second_word; struct s-def *next; } t_def
typedef struct s_type { t_def *first; } t_list;
Next, I try to initialize my list. I make a function that work like this :
t_list *list;
t_def *words;
list = malloc(sizeof(*list));
words = malloc(sizeof(*words));
if (list == 0 || words == 0)
return (NULL);
words = NULL;
words->next = NULL;
list->first = words;
return (list);
Precision : I try to make an empty list for now, so that the user can add some elements later.
And that's where it block : when I run the program, it gives the typical Segmentation Fault. But it don't see what's wrong with what I made ! I put some write in my function to retrace the process : the malloc are working ok, as well as the words = NULL, but then the segment fault seems to run at the line
words->next = NULL;
What do I make wrong ? Why can't I give a NULL value at the next of my words ?
You first initialize the word pointer with allocated memory
words = malloc(sizeof(*words));
Then 3 lines down you set that pointer to NULL again, creating a memory leak
words = NULL;
And then you try to dereference the pointer that you just set to NULL:
words->next = NULL;
So, just remove the words = NULL;
The problem is most likely this part:
words = NULL;
words->next = NULL;
Here you reassign the pointer words to be a null pointer, and directly afterwards you dereference this null pointer, leading to undefined behavior.
When you set words to NULL, you have made a null pointer. Trying to access it immediately afterwards by words->next is effectively doing NULL->next which will cause an error.
Your code looks a little more complex than it needs to be for a simple linked list implementation, you might try something like:
typedef struct s_element
{
char* firstWord;
char* secondWord;
s_element* next;
} t_element;
t_element* list = NULL;
t_element* addFront(t_element* list, char* word1, char* word2)
{
t_element* next = list;
list = malloc(sizeof(t_element));
if (!list) return NULL;
list->firstWord = word1;
list->secondWord = word2;
list->next = next;
return list;
}
Assuming I haven't made any bone-headed syntax mistakes, this should be about as clear as a linked list can get. Notice that it doesn't need to check if the list is empty, the only conditional is in case malloc has failed.
I have tested both the addNode function and the printf loop with different lists and they work fine. But something is wrong with this one because when I print the list it rints the head and then all the other words are the same with the last given from the user.
This is the addnode(the word is given from the user-I checked it, it works fine)
struct list* addNode(struct list* head, char *word){
struct list *curr,*help,*Nhead;
curr=(struct list *)malloc(sizeof(struct list));
curr->sorted=head->sorted;
if(curr->sorted==false){
Nhead=head;
while(head->next!=NULL){
head=head->next;
}
curr->data.word=word;
curr->prev=head;
curr->next=NULL;
head->next=curr;
}
else{
Nhead=head;
for(help=head; help!=NULL; help=help->next){
if(strcmp(word,help->data.word)<0){
break;
}
}
if(help==NULL){
for(help=head; help->next!=NULL; help=help->next){}
curr->next=NULL;
curr->data.word=word;
curr->prev=help;
help->next=curr;
}
else{
curr->next=help;
curr->prev=help->prev;
curr->data.word=word;
help->prev=curr;
if(help!=head){
help=curr->prev;
help->next=curr;
}
else{
Nhead=curr;
}
}
}
return Nhead;
}
and this is how i print
for(curr=pathWordsH; curr!=NULL; curr=curr->next){
printf("%s",curr->data.word);
if(curr->next!=NULL){
printf("-->");
}
}
You don't show how you read in the words, but I guess this is what happens:
When you take input from the user, you probably use the same char buffer. You assign that char buffer to your list node's word and it holds the current word while you're constructing the list. After you are done, all nodes reference the same char buffer, whose contents are the last word the user provided.
You should therefore copy the contents instead of assigning pointers. There are basically two ways of doing this: Either allocate memory to list->word for each node or make the word entry a fixed-size buffer.
The (non-standard, but widely available) function strdup duplicates a string on the heap. So instead of assigning the pointer
curr->data.word = word;
assign a copy of the contents:
curr->data.word = strdup(word);
Because you have allocated extra memory, you should free the memory in word when you destroy a list node.
I'm trying to create a linked list with a head and a tail node in C. Each node needs to hold an int and a string. My issue is that when I create a new node, assign it the correct values, and add it to the end of the list. All previous nodes obtain the string that I assigned the newest node. The int values stay correct, but its like their char pointer gets reassigned. I'm guessing I'm doing somethign wrong with pointers and have looked at tons of examples online and can't see where I'm going wrong. I included my node structure and add function. Thanks for all the help!!
// Linked list (queue) for requests
typedef struct request {
struct request *next;
int request_id;
char *command;
} request_t;
// Global variables
request_t *head = NULL;
request_t *tail = NULL;
void add(int r_id, char *c) {
request_t *node = NULL;
node = (request_t *)malloc(sizeof(request_t));
node->request_id = r_id;
node->command = c;
node->next = NULL;
if(head == NULL) {
head = node;
tail = node;
} else {
tail->next = node;
tail = node;
}
}
You need to create a duplicate of the string.
i.e. in the add function you require the line
node->command = strdup(c);
In addition you will have to free this string to prevent memory leaks when you free the node.
node->command = c;
Should be something like:
// Allocate enough memory for a copy of the string pointed to by c
node->command = malloc(strlen(c) + 1);
// Copy the string pointed to by c, to the newly allocated memory
strcpy(node->command, c);
Or since you have to call strlen on c you can use the more efficient:
size_t num_bytes = strlen(c) + 1;
// Allocate enough memory for a copy of the string pointed to by c
node->command = malloc(num_bytes);
// Copy num_bytes bytes from the memory pointed to by c, to the newly allocated memory
memcpy(node->command, c, num_bytes);
So that you make a copy of the string. Your delete function should be modified to free the allocated memory as well:
free(node->command);
So what was wrong with your code?
Without copying the string you just copy the pointer, so if you call add(10, somestr); Then somestr is a char * (a pointer). If you modify the string at that memory location it will also be modified in your linked list since there is really only one string with two pointers to it (command and somestr).
I never knew one could assign a string to another using a '=' operator :P This may work fine with structure variables but not with strings. Try using strcpy(node->command, c) after dynamically allocating size to node->command. This should work! :)
I am only allowed to use following headers
#include <stdio.h>
#include <stdlib.h>
and I defined my struct student as following:
struct dict
{
char* word;
struct dict* link;
};
There are many functions, but only one function that I have problem right now.
This function inserts a struct dict with certain name at the end of the link.
struct student *Linsert(struct dict *list, char *name)
{
struct student *pnew;
struct student *pn;
int exist = 1;
pnew = (struct dict *)malloc(sizeof(struct dict));
pnew -> next = NULL;
pnew -> name = name;
if (list != NULL)
{
for (pn = list; pn -> next != NULL; pn = pn -> next) ;
pn -> next = pnew;
}
else
list = pnew;
return list;
}
Using the following function,
//print all the values in the list
void printList(struct dict* list);
I did this:
int main(void)
{
struct dict *list = NULL;
char *name;
while (1) {
scanf("%s", name);
if (name == 'Q')
break;
list = Linsert(list, name);
printList(list);
}
return 0;
}
Lets say for input, I typed three
apple banana and orange, my result shows three of my last input.
What is the issue here?
I see two problems with your code:
You need to pass scanf a char array of size sufficient to store the input string, not simply a char pointer.
You need to copy strings passed into Linsert (use strdup).
Your main snippet alone has a whole bunch of problems:
name is an uninitialized pointer; it's pointing to some unknown location in memory that you haven't allocated and are allowed to use, therefore causing undefined behaviour. Perhaps you want char name[20] to allocate an array of 20 chars on the stack, and have scanf store the input in this buffer.
You're comparing a char* (a pointer to the start of a string) to a single char 'Q' - you're comparing a pointer and an integer value as your compiler warnings will tell you. You're not comparing the contents of the string for the value 'Q', you're comparing the memory address of name and the integer value for 'Q'. If you want to compare the string name with the string "Q", use strcmp and check for a return value of 0.
You also want to be making a copy of the variable passed to Linsert otherwise, as you've noticed, you'll be passing a pointer to the same location in memory every time, and a change to this block of memory will change each of your items.
If you turn your compiler warnings up you'll get even more warnings.
You are not allocating any memory for the name, so scanf is writing to some random location, and overwriting that every time through the loop.
One issue is that you've not allocated storage for the word member to point to. You have also not allocated space for name to point at. That is a principle cause of trouble.
You need to allocate space for name; the easiest way is:
char name[128];
You need to allocate space to store the word, and you need to copy the contents of name into the word so that when the next line overwrites name, it does not destroy the saved word.
Adapting your code, you might use:
struct student *Linsert(struct dict *list, char *name)
{
struct student *pnew;
struct student *pn;
pnew = (struct dict *)malloc(sizeof(struct dict));
if (pnew == 0)
...error...
pnew->next = NULL;
pnew->word = malloc(strlen(name) + 1);
if (pnew->word == 0)
...error...
strcpy(pnew->word, name);
if (list != NULL)
{
for (pn = list; pn->next != NULL; pn = pn->next)
;
pn->next = pnew;
}
else
list = pnew;
return list;
}
Do not omit the error checks on memory allocation - painful though it be. It will bite you when you forget.
Stylistically, do not use spaces around either -> or .; they are operators that bind very tightly, and they should not be spaced out like other binary operators.
There's a convenient function, strdup(), the duplicates a string, but it is not standard C (it is standard POSIX).
Since name is a char pointer, your assignment to the field of each dict struct will use the latest value it points to.