what is wrong with this flushing function for hash table? - c

I have a weird problem with flushing the entire hash-table.
Data structures are as below:
typedef struct data_entry_{
char data[32];
struct data_entry_ *next;
}data_entry_t;
typedef struct table_entry_{
char hash[32];
struct data_entry_ *next_data;
struct table_entry_ *next_hash;
}table_entry_t;
typedef struct table_{
table_entry_t *next;
}table_t;
In main function, I initialize the table with below function
table_t *init(){
table_t *table = calloc(1, sizeof(table_t));
table_entry_t *node = calloc(1, sizeof(table_entry_t));
node->next_hash = NULL;
node->next_data = NULL;
strcpy(node->hash, "NULL");
table->next = node;
return table;
}
Add data to the table with below function:
int add(table_t *table, char *data){
table_entry_t *head = table->next;
table_entry_t *prev;
char hash[32];
hash_function(data, hash);
if(!strcmp(head->hash, "NULL")){
data_entry_t *item = calloc(1, sizeof(data_entry_t));
strcpy(item->data, data);
item->next = NULL;
strcpy(head->hash, hash);
head->next_data = item;
head->next_hash = NULL;
return 0;
}
while(head){
if(!strcmp(head->hash, hash)){
data_entry_t *temp = head->next_data;
data_entry_t *previous;
while(temp){
if(!strcmp(temp->data, data)){
printf("data exists\n");
return 0;
}
previous = temp;
temp = temp->next;
}
data_entry_t *item = calloc(1, sizeof(data_entry_t));
strcpy(item->data, data);
item->next = NULL;
previous->next = item;
return 0;
}
prev = head;
head = head->next_hash;
}
table_entry_t *pack = calloc(1, sizeof(table_entry_t));
data_entry_t *item = calloc(1, sizeof(data_entry_t));
strcpy(pack->hash, hash);
strcpy(item->data, data);
item->next = NULL;
pack->next_data = item;
prev->next_hash = pack;
return 0;
}
And the problem is with this function:
int flush(table_t *table){
table_entry_t *head = table->next;
table_entry_t *temp;
data_entry_t *current, *previous;
if(head->next_data == NULL){
printf("table is empty\n");
return -1;
}
strcpy(head->hash, "NULL");
while(head){
current = head->next_data;
while(current){
previous = current;
current = current->next;
free(previous);
}
temp = head;
head = head->next_hash;
free(temp);
}
return 0;
}
after calling flush, when I wanna show the table, I expect to see "table is empty" but apparently this function does not free any nodes. I really appreciate if anyone help me.

You expect that a pointer which got freed gets the value NULL.
That however is impossible, because free() only receives the value inside the pointer, not the address, i.e. not pointer to the pointer.
Your code assumes that a freed pointer is NULL here:
if(head->next_data == NULL){
That however will only be the case if you write the NULL value to the pointer.
There is some code which seems similar, strcpy(head->hash, "NULL");, but I trust that you do not expect that to write the non-string value NULL to the pointer.
You can make sure that all pointer which you free afterwards contain NULL, by writing it yourself. Near this free(previous);. It is however necessary to write NULl to the actual pointer in the linked list - and not e.g. to the variable previous. You can use the copy of the pointer stored in that variable to free, but not for overwriting the original.

Related

Why is there junk data in the first node of my linked list?

It appears that there is junk data in the first node of my list. Why would this be?
These are the definitions of the structs i'm using.
typedef struct node {
char *x;
struct node *next;
}node;
typedef struct {
struct node *head;
}list;
// create_list() function :
list* create_list(){
list *myList = malloc(sizeof(myList));
myList->head = NULL;
if (myList->head != NULL){
return NULL;
}
return myList;
}
Here is the implementation of the add_to_list function
int add_to_list(list* ll, char* item){
node *current = ll->head;
node *new_node = malloc(sizeof(node));
if (!new_node){
fprintf(stderr, "error allocating mem.\n");
return 1;
}
strcpy(new_node->x, item);
new_node->next = NULL;
if(ll->head == NULL){
ll->head = new_node;
return 0;
}else{
while(current->next){
current = current->next;
}
current->next = new_node;
}
return 0;
}
This is the print_list(); funcntion
void print_list(list *ll){
node *current = ll->head;
while(current){
printf("%s\t\n",current->x);
current = current->next;
}
}
when I call the function in main.c here is how i'm doing it :
list *newList = create_list();
char test_var = 'k';
add_to_list(newList, &test_var);
printf("printing whole list : \n");
print_list(newList);
It is because you are passing a char as a char pointer (ie a string).
Change
char test_var = 'k';
to
char *test_var = "k";
and change the call to
add_to_list(newList, &test_var)
to
add_to_list(newList, test_var)
regarding this statement:
strcpy(new_node->x, item);
the 'x' field is a uninitialized pointer. so using that pointer to point to the destination area is undefined behavior.
Writing to where a uninitialized pointer points could result in a seg fault event.
AND is the reason for the corrupted data. Your just lucky a seg fault didn't occur nor any of the other data being corrupted.
if you know the max length of the data, then you could change the struct definition so the field 'x' is an array of char rather than a pointer.
Otherwise, suggest using something similar to:
new_node->x = strdup( data );
if( !new_node->x )
{ // then strdup() failed
perror( "strdup failed" );
// call a cleanup function here
free( new_node );
exit( EXIT_FAILURE );
}
// implied else, strdup successful

Global pointer (head of linked list) not updated properly

I have searched a lot, before I ask this, but I can't get this small piece of code to work.
I know that using a global pointer (or variable) is considered a bad practice (instead of passing by reference) but I am forced to use this practice sadly.
What I am trying to do is to make a linked list which consists of nodes (struct with some info), and after every insert() the list is dynamically expanded by one node (unless the element in question already exists, in that case the member name is overwritten).
The pointer next points to the next element in the list (that's where I assign the new node from malloc().
The program compiles correctly and executes with the following output:
retrieve returned: (NULL) at every printf() call
That's why I believe the global pointer (head of the list) is not updated properly
I am sorry for this naive question but I can't seem to find where the assignment/allocation goes wrong, Anyway thanks in advance for your assistance.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct node{
char *id;
char *name;
struct node *next;
};
struct node* list; //list head
struct node* p; //pointer to list head
char *retrieve(char *id){
if(list == NULL)
return NULL; //list is empty, no element to return.
for(p = list; p != NULL; p = p->next)
if(strcmp(id, p->id) == 0)
return p->name;
return NULL;
}
void insert(char *id, char *name){
int exists = 0;
struct node* temp = NULL;
for(p = list; p != NULL; p = p->next){
if(strcmp(id, p->id) == 0){ //id already exists, overwrite with the new name.
free(p->name);
p->name = strdup(name);
exists = 1;
break;
}
}
if(exists) return;
//insert at the end of the list
temp = malloc(1 * sizeof(struct node));
if(temp == NULL){
printf("memory allocation failed\n");
return;
}
temp->id = strdup(id);
temp->name = strdup(name);
temp->next = NULL;
p = temp;
return;
}
int main(){
struct node* temp = NULL;
p = NULL;
list = NULL;
insert("145a","Jim");
insert("246b","Alice");
insert("322c","Mike");
printf("retrieve returned: %s\n\n", retrieve("145a"));
printf("retrieve returned: %s\n\n", retrieve("246b"));
printf("retrieve returned: %s\n\n", retrieve("322c"));
p = list;
while(p != NULL){ // node deletion starting from first to last element.
free(p->id);
free(p->name);
temp = p;
p = p->next;
free(temp);
}
return 0;
}
void insert(char *id, char *name)
{
struct node *temp = NULL, **pp;
/* Pointer to pointer points to the global */
for(pp = &list; *pp ; pp = &(*pp)->next){
if(strcmp(id, (*pp)->id) ) continue;
free((*pp)->name);
(*pp)->name = strdup(name);
return;
}
//insert at the end of the list
temp = malloc(sizeof *temp);
if(!temp ){
printf("memory allocation failed\n");
return;
}
temp->id = strdup(id);
temp->name = strdup(name);
temp->next = NULL;
*pp = temp;
return;
}
And you can even do without the *temp pointer:
void insert(char *id, char *name)
{
struct node **pp;
for(pp = &list; *pp ; pp = &(*pp)->next){
if(strcmp(id, (*pp)->id) ) continue;
free(p->name);
p->name = strdup(name);
return;
}
// pp now points to the terminal NULL pointer
*pp = malloc(sizeof **pp);
if(!*pp ){
printf("memory allocation failed\n");
return;
}
(*pp)->id = strdup(id);
(*pp)->name = strdup(name);
(*pp)->next = NULL;
return;
}
You never initialize list other than with NULL. In consequence,
char *retrieve(char *id){
if(list == NULL)
return NULL;
always returns NULL.

Adding node to singly linked list not working properly

I have a singly linked list struct and a local Node declared in main with my addToList() function, but every time addToList() executes it runs the case (head == NULL). Even when I already added values to the list. I am sure there is a small bug in my code, I just can't find it.
typedef struct node{
char* name;
int groupSize;
int status;
struct node* next;
}Node;
void addToList(char* name, Node* head, int groupSize, int status){
if(head == NULL){
head =(Node*)malloc(sizeof(Node));
head->name = (char*) malloc(sizeof(char)*30);
strcpy(head->name, name);
head->groupSize = groupSize;
head->status = status;
head->next = NULL;
printf("Name is %s\n\n", head->name);
}
else {
printf("entered else\n");
Node *tmp = head;
if(tmp->next!=NULL){
tmp = tmp->next;
}
tmp->next = (Node*) malloc(sizeof(Node));
tmp->next->name = (char*) malloc(sizeof(char)*30);
strcpy(tmp->next->name, name);
tmp->next->groupSize = groupSize;
tmp->next->status = status;
tmp->next->next = NULL;
}
}
int main(){
Node* head = NULL;
//TESTNG SECTION
addToList("Julio", head, 5, 7);
addToList("Francisco", head, 5, 7);
addToList("Jorge", head, 5, 7);
}
The cause of your problem is the call by value of every C function call.
At the first call of addToList(), head of the main() points to NULL.
Inside the addToList() you change the parameter head to point to a newly allocated memory region. Unfortunately, by the time addToList() returns the scope of the parameter head does not exist any more. So the head variable at the main, has exactly the same value prior the addToList() call.
The solution to this problem would be the use of double pointer for the head, or return and assign at every call the head of the list. So because previous answer covered the one solution, I will provide the other.
Node* addToList(char* name, Node* head, int groupSize, int status){
if(head == NULL){
head =(Node*)malloc(sizeof(Node));
head->name = (char*) malloc(sizeof(char)*30);
strcpy(head->name, name);
head->groupSize = groupSize;
head->status = status;
head->next = NULL;
printf("Name is %s\n\n", head->name);
}
else {
printf("entered else\n");
Node *tmp = head;
if(tmp->next!=NULL){
tmp = tmp->next;
}
tmp->next = (Node*) malloc(sizeof(Node));
tmp->next->name = (char*) malloc(sizeof(char)*30);
strcpy(tmp->next->name, name);
tmp->next->groupSize = groupSize;
tmp->next->status = status;
tmp->next->next = NULL;
}
return head;
}
int main(){
Node* head = NULL;
//TESTNG SECTION
head = addToList("Julio", head, 5, 7);
head = addToList("Francisco", head, 5, 7);
head = addToList("Jorge", head, 5, 7);
}
The head parameter will be initialized inside the function, you have to pass a pointer to a Node pointer :
void addToList(char* name, Node** head, int groupSize, int status);
Then :
*head =(Node*)malloc(sizeof(Node));
Explanations by Manos.
There are a few small problems that I see with your code.
First, I would use different names when passing in the head pointer. It might be confusing the compiler on where to look. Also, when you pass in a node pointer in addToList, you need to dereference it, so instead pass in Node**, and initialize a pointer within the method.
Second, in your addToList method, you have the following condition:
if(tmp->next!=NULL){
tmp = tmp->next;
}
this will only work when you have 2 elements in your list. Instead, make it a while loop, like so:
while(tmp->next!=NULL){
tmp = tmp->next;
}
This will correctly place the tmp pointer at the end of your list.
Hopefully this will fix some bugs,

singly linked list error while adding a node

My problem is that, when i try to add a node to my singly linked list for the first time, everything goes expected, but when i want to add another node, my program crashes. Seems like my error is caused by trying to write to 0 address. however i can't seem to find the error in my code. am i using malloc right ?
Here is the code :
typedef struct linkedList
{
int StudentId;
char name[100];
char dep[100];
struct linkedList *next;
} LinkedList;
LinkedList *head = NULL;
LinkedList *current = NULL;
LinkedList *createList(int val, char name[], char dep[])
{
LinkedList *ptr = (LinkedList *)malloc(sizeof(LinkedList));
if (ptr == NULL)
{
printf("Node Creation Failed\n");
return NULL;
}
ptr ->StudentId = val;
strcpy(ptr -> name, name);
strcpy(ptr ->dep, dep);
ptr ->next = NULL;
head = current = ptr;
return ptr;
}
LinkedList *addToList (int val, char name[], char dep[])
{
if (head == NULL)
{
return (createList(val, name, dep));
}
else
{
LinkedList *ptr = (LinkedList *)malloc(sizeof(LinkedList));
if (ptr = NULL)
{
printf("Node Creation Failed\n");
return NULL;
}
ptr -> StudentId = val;
strcpy(ptr ->name, name);
strcpy(ptr ->dep, dep);
ptr -> next = NULL;
current -> next = ptr;
current = ptr;
return ptr;
}
}
in main function:
AddtoList(10,"abc","abc");
calls createList, no problem but
If i use AddtoList again, program crashes, createList and AddtoList are really similar to each other, can't figure out what the problem is.
Change
if (ptr = NULL)
to
if (ptr == NULL)
In your case, ptr is assigned to NULL and expression within if is evaluated to 0. Control goes to ptr -> StudentId = val;. It tries to access write protected memory, hence the crash.

Linked List inserting trouble

I am having trouble with inserting items in linked list. All the elements end up having the same *data content that is inserted in the last. The program compiles successfully. I use gcc and gdb.
I am new to coding so please mention troubles if any that you see in my programming style.
typedef struct Node{
void* data;
struct Node* next;
} *node;
node allocate(){
node current = malloc(sizeof(struct Node));
current->data = NULL;
current->next = NULL;
return current;
}
void insert(node *head, void *data){
// if head has no data then set the data on head and return
if(((*head)->data == NULL)&&((*head)->next == NULL)){
(*head)->data = data;
return;
}
node newHead = allocate();
newHead->data = data;
newHead->next = *head;
*head = newHead;
//printf("Node Data : %d\tNext Node Data : %d",
//*(int *)((*head)->data), *(int *)((*head)->data));
}
int main(int argc, char *argv[]){
node head = allocate();
int count = inputSequence(&head);
int *aod = calloc((size_t) count, sizeof(int));
generateAOD(head, aod);
if(checkJolly(aod, count) == TRUE)
printf("Jolly\n");
else
printf("Not Jolly\n");
return 0;
}
int inputSequence(node *input){
int *num = malloc(sizeof(int));
int count = 0;
while((scanf("%d", num) != EOF)){
insert(input, (void *)num);
count++;
}
traverse(*input, fn);
return count;
}
Your insert logic is non-existant. And you're literally making your life harder by attempting to manage a linked list in the fashion you're using.
The head pointer itself should indicate whether the list is empty. If it is NULL, its empty. If it isn't, there's data. Code the insertion logic accordingly.
And your inputSequence is utterly broken. It only allocates ONE data point, then uses the same data allocation for every insertion. You need one for each insertion.
First, change allocate() to accept the data being inserted. It will make the remaining code less cluttered:
node allocate(void *data)
{
node current = malloc(sizeof(*current));
current->data = data;
current->next = NULL;
return current;
}
Second, insert by allocating a new node as needed.
void insert(node *head, void *data)
{
node p = allocate(data);
p->next = *head;
*head = p;
}
Next, fix inputSequence() to properly allocate memory for each entry:
int inputSequence(node *input)
{
int count = 0;
int num = 0;
// note: check for number of params *successfully* parsed.
// if it isn't 1, its time to leave the loop.
while(scanf("%d", &num) == 1)
{
int *data = malloc(sizeof(num));
*data = num;
insert(input, data);
++count;
}
return count;
}
And lastly, make sure your head pointer is initially NULL in main().
int main(int argc, char *argv[])
{
node head = NULL;
// load linked list
inputSequence(&head);
// ... the rest of your code....;
return 0;
}
With the above, the logical answer of "Is my list empty" is simply if (!head) Further, this makes things like traversal trivial.
void traverse(node ptr, void (*pfn)(void *))
{
while (ptr)
{
pfn(ptr->data);
ptr = ptr->next;
}
}
Freeing the list is equally trivial:
void destroy(node *head)
{
while (*head)
{
node p = *head;
*head = p->next;
free(p->data);
free(p);
}
}
typedef struct Node{
void* data; // Make it as int or any other data type
struct Node* next;
} *node;
In function inputSequence() you are allocating memory for num at the very beginning and making node->data point to this memory each time you are adding a node. Hence data pointer of each node of your linked list points to the same memory location and hence contains the same value.
If you still want to continue with data as a void pointer, allocate num for each iteration of the while loop and pass this to insert function.
while((scanf("%d", num) != EOF)){
num = malloc(sizeof(int);
insert(input, (void *)num);
count++;
}

Resources