So I'm trying to do a method to clear a doubly linked list for school where the doubly linked list and nodes are defined as:
struct word_entry
{
char *unique_word ;
int word_count ;
} ;
struct node
{
struct word_entry one_word ;
struct node *p_previous ;
struct node *p_next ;
} ;
struct linked_list
{
struct node *p_head ;
struct node *p_tail ;
struct node *p_current ;
} ;
I have a method to clear a linked list by doing
int clear_linked_list( struct linked_list *p_list ) //return how many nodes were cleared
{
if (p_list->p_head == NULL) {
return 0;
}
else {
int count = 0;
struct node *curr = p_list->p_head;
while (curr != NULL) {
struct node *next = curr->p_next;
free(curr->one_word.unique_word);
free(curr);
curr = next;
count++;
}
return count;
}
}
I do a free() on curr->one_word.unique_word because it's a malloc'd char array. I was taught to free when I use malloc, so that's there.
The issue I run into is I get a "bogus pointer (double free?)" and a core dump when I run the test file provided by my professor. I've worked on this for a few hours and can't seem to find out where (or how) I'm calling free twice.
When you loop through the list, you should constantly change the position of the head, so that even if you repeat clear_linked_list, you will not get an error.
int clear_linked_list(struct linked_list* p_list) // return how many nodes were cleared
{
if (p_list->p_head == NULL) {
return 0;
} else {
int count = 0;
while (p_list->p_head != NULL) {
struct node* curr = p_list->p_head;
p_list->p_head = p_list->p_head->p_next;
free(curr->one_word.unique_word);
free(curr);
count++;
}
return count;
}
}
When freeing memory it is a good practice to set NULL to pointers that were freed to avoid this kind of problems.
So you should do:
free(curr->one_word.unique_word);
curr->one_word.unique_word=NULL;
//if that one_word.unique_word was shared between multiples nodes that free could cause problems if you dont set it to NULL afterwards
free(curr);
curr=NULL; //or curr=next...
Also. Check that when you create the nodes that:
*p_next is NULL on the last node of the double linked list
*p_previous is NULL on the first node of the list
You don't null out p_head before you leave the clear function.
So, if you called it twice, you'd have problems (i.e. p_head would point to an already freed node). Likewise for p_tail.
Also, if you tried to add to the list again, you'd have similar problems.
Otherwise, your clear code is just fine.
So, can you prove that the list is constructed correctly (e.g. before you free, add a printf that prints out all the node's pointers before you free anything).
Related
I'm new to C.I am trying to create a doubly linked list where the data field is a structure. But when I output the elements, only the first field of the structure is correctly displayed.
struct n
{
int a;
int b;
};
typedef struct _Node {
struct n *value;
struct _Node *next;
struct _Node *prev;
} Node;
typedef struct _DblLinkedList {
size_t size;
Node *head;
Node *tail;
} DblLinkedList;
DblLinkedList* createDblLinkedList() {
DblLinkedList *tmp = (DblLinkedList*) malloc(sizeof(DblLinkedList));
tmp->size = 0;
tmp->head = tmp->tail = NULL;
return tmp;
}
void pushBack(DblLinkedList *list, struct n *value) {
Node *tmp = (Node*) malloc(sizeof(Node));
if (tmp == NULL) {
exit(3);
}
tmp->value = value;
tmp->next = NULL;
tmp->prev = list->tail;
if (list->tail) {
list->tail->next = tmp;
}
list->tail = tmp;
if (list->head == NULL) {
list->head = tmp;
}
list->size++;
}
void printInt(struct n *value) {
printf("%d, %d", value->a, value->b);
}
void printDblLinkedList(DblLinkedList *list, void (*fun)(struct n*)) {
Node *tmp = list->head;
while (tmp) {
fun(tmp->value);
tmp = tmp->next;
printf("\n");
}
}
So, I have a few questions. Did I declare the node value field correctly? Am I inserting the node at the end of the list correctly? Am I doing the output of doubly linked list items correctly? And where is my mistake and how to fix it?
Did I declare the node value field correctly?
That depends on what your intention was. In terms of storing a pointer to a struct n: yes.
Am I inserting the node at the end of the list correctly?
Yes.
Am I doing the output of doubly linked list items correctly?
Yes.
And where is my mistake and how to fix it?
The code works from my point-of-view but what can be misleading is how pushBack operates. pushBack takes the struct n pointer as-is and stores it in Node. You did not post the pushBack caller code but the current implementation can caused problems if the caller assumes that the struct n gets copied.
To illustrate that, consider the following:
struct n value;
value.a = 1;
value.b = 2;
pushBack(list, &value);
value.a = 3;
value.b = 4;
pushBack(list, &value);
By reusing the value, two linked list nodes will effectively contain the same values. Also, the inserted struct n pointer must remain valid throughout the lifetime of the list. So, inserting stack-allocated values (that will be deallocated later by leaving their scope) or freeing dynamically-allocated values too early might lead to incorrect values. As long as the caller knows that, this is not necessarily a problem.
There are usually 3 ways to handle memory ownership:
Data structure owns values (just like it owns nodes) and is responsible for freeing them
Data structure copies values and is responsible for freeing them
Caller owns values and is responsible for freeing them
For a linked list, there's lots of merit in the strategy #3, because a linked list can be created from existing values without any copying or ownership transfer which would most certainly require changes to existing code. That's basically what your code is doing at the moment.
I am a new C99 programmer and want the help of the community on this one.
I wrote the following function which receives two pointers for a node (Or simply Node) and a pointer to a pointer to node (Or *Node) an merges them together into one sorted Node.
This is Given:
typedef struct node_t {
int x;
struct node_t *next;
} *Node;
typedef enum {
SUCCESS = 0,MEMORY_ERROR, EMPTY_LIST, UNSORTED_LIST, NULL_ARGUMENT,
} ErrorCode;
int getListLength(Node list);
bool isListSorted(Node list);
This is the code I wrote:
ErrorCode mergeSortedLists(Node list1, Node list2, Node *merged_out)
{
if (merged_out==NULL)
return NULL_ARGUMENT;
if (list1==NULL || list2==NULL)
return EMPTY_LIST;
if (!isListSorted(list1) || !isListSorted(list2))
return UNSORTED_LIST;
Node ptr=*merged_out;
int list1_len=getListLength(list1),list2_len=getListLength(list2);
for (int i=0;i<list1_len+list2_len;i++)
{
int min=0;
if (list1!=NULL && (list2==NULL || (list2!=NULL && list1->x<=list2->x))){
min = list1->x;
list1=list1->next;
}
else{
min=list2->x;
list2=list2->next;
}
ptr->x=min;
if (i==list1_len+list2_len-1){
ptr->next=NULL;//The next for the last Node should be Null
continue;
}
ptr->next=malloc(sizeof(*ptr));
if (ptr->next==NULL){
//We should Free all previosly allocated memory
//except for the first node since it was not allocated via malloc
return MEMORY_ERROR;
}
ptr=ptr->next;
}
ptr=NULL;
return SUCCESS;
}
But after reviewing my code I was told it has a lot of code duplications (more precisely inside he for loop) which should be corrected by using external functions, do u notice any code duplications? and how to fix that, any ideas?
I've tried to look up the solution to this problem through various other threads but my search was unsuccessful. I'm new to C and also new to this site so I apologize in advance if I'm incorrect in phrasing this question. I kinda have an idea of what's going on, but at the same time I might be entirely wrong. I have a linked list and I'm trying to insert at the end of the list. But when Xcode gets to the statement while(ptr->next!=NULL) it throws the error:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x800000012)
I read somewhere before that it was because I'm accessing something that doesn't exist or I'm failing to initialize node->next to NULL but I did in the previous "if statement". I'm pretty new at coding with pointers and linked lists and again I apologize for any weird stuff that might be in my code ):
////LIST AND NODE STRUCTURES
//data nodes
typedef struct node{
int data;
int ID;
struct node* prev;
struct node* next;
} node;
typedef struct ListInfo{
int count; //numnodes
struct node *list; //list of nodes
} ListInfo;
////INSERT FUNCITON
void insert(ListInfo *H, node *n){
if(n == NULL)
return;
node* ptr = H->list;
if(H==NULL){
ptr = n;
ptr->next = NULL;
}
else{
while(ptr->next!=NULL){ //Thread 1: EXC_BAD_ACCESS (code=1, address=0x800000012)
ptr = ptr->next;
}
ptr = n;
ptr->next = NULL;
}
// End of function
return;
}
////MAIN
int main(){ // No Edititng is needed for the main function.
ListInfo H;
H.count =0;
node *n;
int Data = 0,ID =0 ;
do{
printf("Enter an ID and a Value to add to the list, Enter -1 to stop: ");
//Get value from user to store in the new linked list node later.
scanf("%d %d",&ID,&Data);
// Check if the user entered "-1", if so exit the loop.
if(Data == -1||ID == -1)
return 0;
// Allocate memory for a new Node to be added to the Linked List.
n = malloc(sizeof(node));
// Put the Data from the user into the linked list node.
n->data = Data;
n->ID = ID;
//Increment the number of nodes in the list Header.
// If the current node count is zero, this means that this node is the first node
// in this list.
if(H.count++ == 0)
H.list = n;
// Otherwise, just use the insert function to add node to the list.
else insert(&H,n);
}while(Data != -1);
// Display all nodes in the list.
DisplayList(&H);
//Remove a node from the list, and display the list each time.
while(H.count != 0){
Delete(&H,H.list->data);
DisplayList(&H);
}
// Display the list, this should be empty if everything was correct.
DisplayList(&H);
}
When you allocate n you never set n->next. When you pass it to insert() you try to access the bad pointer and crash. When you set n->ID you should set n->next to NULL.
I am trying to work with a doubly linked list of SAT grades between 200-800. I need to remove from all the duplicates from the list, i.e. make sure each grade appears only once by deleting all its duplicates.
#define HIGHEST_GRADE 800
typedef struct dListNode{
int* dataPtr;
struct dListNode* next;
struct dListNode* prev;
}DListNode;
typedef struct dList
{
DListNode* head;
DListNode* tail;
}DList;
void removeDuplicates(DList* lst)
{
int i;
int gradesBucket [numOfGrades];
DListNode* temp;
temp = lst->head;
for(i=200 ; i<HIGHEST_GRADE ; i++) /*creating 600 buckets - each bucket for a grade*/
gradesBucket[i] = FALSE;
while (temp)
{
if ((gradesBucket [*temp->dataPtr]) == TRUE) /*if current grade has already */
/* appeared earlier on the list */
{
deleteFromList (temp); /*delete that grade's cell*/
}
else
gradesBucket[*temp->dataPtr] = TRUE; /* mark grade bucket as true, meaning */
/* the grade already appeared*/
temp = temp->next; /*moving on to next grade*/
}
}
void deleteFromList(DListNode* toRemove)
{
toRemove->prev->next = toRemove->next;
toRemove->next->prev = toRemove->prev;
deAllocateListCell (toRemove);
}
void deAllocateListCell (DListNode* cell)
{
free (cell->dataPtr);
free (cell);
}
Please help me understand what's wrong.
here's the fixed code, which still doesn't work properly. Now it compiles but nothing is shown on screen. And by the way, I dont need to take care of deleting the head, because the first number can never be a duplicate... but I took care of it in case the head was NULL;
I also send the previous cell of the one i want to delete, to the function deleteFromList. It still doesn't work. Any ideas? Thanks!
void deleteFromList(DList* lst, DListNode* p)
{
DListNode* del_cell = p->next; /* cell to delete*/
if (p->next->next == NULL) /*if cell to remove is the tail*/
{
deAllocateListCell (p->next); /* freeing current tail */
lst->tail = p; /* p is the new tail */
p->next = NULL; /* tail points to NULL */
}
else /* if cell to remove is not the tail (note: can't be head beacuse no duplicates can be found in the first grade) */
{
p->next = del_cell->next;
del_cell->next->prev = p;
deAllocateListCell (del_cell);
}
}
The code of your function deleteFromList() doesn't account for (literal) edge cases: Deleting the first or last node of a list.
Plus, your code dereferences a pointer to a deallocated node; the pointer can become outright invalid, or the free() function can overwrite its contents (as the Microsoft Debug C RunTime is known to).
Try to be specific - What is it that doesn't work? Does your code compile? Do you get an error during runtime? You don't get the results you expected in a scenario?
Your deleteFromList function should take care of removing the head or tail (That is when toRemove->prev or toRemove->next are null (respectively).
temp = lst->head; and what happens when lst is null? You'll get a run-time error
You are not updating head or tail in case they are deleted
That's what I found in first glance.
you should write while(temp->next) for correcting this....and also u can simply deallocate the node using free. and to eliminate dangling pointer problem you should make it NULL after freeing that node
Here is the code for freeing the whole linked list
void free_list(RecordType *list)
{
RecordType *tempNode; /* temporary Node to hold on the value of previous node */
while(list != NULL) /* as long as the listnode doesn't point to null */
{
tempNode = list; /* let tempNode be listNode in order to free the node */
list = list->next; /* let list be the next list (iteration) */
free(tempNode); /* free the node! */
}
}
I think this code itself is working ok (?), but I have no idea how to check.
I only applied the theory (e.g. # of frees must = to the # of mallocs)
So here are some questions that I'm wondering...
Does this method work?
Do I need to malloc tempNode?
I initialized tempNode before while loop... but after I free, tempNode still works... I don't really get that part
The theory that I used:
# of free() == # of malloc()
You need a temporary node to hold the current node
Let the current node equal to the next node
Free the current node by using the temporary node
If any of my theory sounds wrong, please explain!
Thanks!
Does this method work?
Yes, assuming the list nodes were all dynamically allocated and haven't been previously freed
Do I need to malloc tempNode?
You don't need to allocate any memory inside free_list but all list elements must have been dynamically allocated previously. You can only call free on memory that was allocated using malloc (or calloc)
I initialized tempNode before while loop... but after I free, tempNode
still works... I don't really get that part
Calling free returns ownership of memory to the system. It may choose to reuse this memory immediately or may leave it untouched for some time. There's nothing to stop you accessing the memory again but the results of reading or writing it are undefined.
If you want to make it harder for client code to accidentally access freed memory, you could change free_list to NULL their pointer
void free_list(RecordType **list)
{
RecordType *tempNode;
while(*list != NULL) {
tempNode = *list;
list = tempNode->next;
free(tempNode);
}
*list = NULL;
}
If you also want to check that you really have freed all memory, look into using valgrind. This will report any memory leaks and also flags some types of invalid memory access.
The method certainly works - but it should be mallocd first before freeing. Otherwise it is undefined behavior.
You don't need to malloc() tempNode only if list has been previously malloc()d.
The third part is undefined behavior. After free() the data may still exist, but is flagged for being overwritten. You cannot rely on the node once it is free()d
The best way to check your code is interactive tracing by means of Debugger. Gdb in KDevelop on Linux or MS Visual Studio's debugger on MS Windows are perfect. I'll use the later for this demonstration.
This code defines a uni-directed list of integers with three functions: ListPush() adds an integer to the list, ListPrint() displays the list contents and ListDestroy() destroys the list. In main() I insert 3 integers into the list, print them and destroy the list.
#include <malloc.h>
#include <stdlib.h>
#include <stdio.h>
typedef struct Node NODE, *PNODE;
typedef struct Node {
int item;
PNODE next;
};
PNODE ListPush(PNODE head, int item) {
PNODE p;
PNODE n = (PNODE) malloc(sizeof(NODE));
if ( !n ) exit(1);
n->next = 0;
n->item = item;
if (!head) {
head = n;
}
else {
for ( p=head; p->next != 0; p=p->next );
p->next = n;
}
return head;
}
void ListPrint(PNODE head) {
PNODE p;
printf("List contents:\n\n");
for (p=head; p!=0; p=p->next) {
printf("%d ", p->item );
}
}
void ListDestroy( PNODE head ) {
PNODE n, c = head;
if ( !head ) return;
do {
n = c->next;
free(c);
c = n;
} while (c );
}
int main() {
int i;
int a[3] = {1,2,3};
PNODE head = 0;
for ( i = 0; i<3; ++i ) {
head = ListPush(head, a[i]);
}
ListPrint(head);
ListDestroy(head);
return 0;
}
Three attached images illustrate 2 stages of the program (MSVS2012 Debugger).
The first shows state of relevant local vars after for() cycle finishes. Look at head variable and proceed on the tree. You can see three nodes with their contents: integers 1,2 and 3 respectively.
The second image shows the variables inside ListDestroy() after first call to free(). You can see that head points to freed memory (red circles) and pointer in variable c points to the next node being destroyed on the next loop.