Pointer is not being passed during a linked list swap - c

This will most likely seem like I am missing something obvious but when I try to pass a Linked List pointer to my Selection sort I get a NULL pointer problem. In my C code I have this as my linked list:
typedef struct iorb
{
int base_pri;
struct iorb *link;
char filler[100];
} IORB;
Then I pass my new linked list into this function after creating it:
void swapNodes(POINTER *head, POINTER CurrentHead, POINTER CurrentMinimum, POINTER TempSwap);
POINTER SortList(POINTER *head, char *SortMethod[])
{
POINTER TempHead = *head;
//Only one node, no need to sort.
if (TempHead->link == NULL)
{
return head;
}
//Store node with the new minimum value.
POINTER TempMin = *head;
//Store curent node for swapping
POINTER TempSwap = *head;
//Transverse the list.
POINTER TempCurrent;
for (TempCurrent = *head; TempCurrent->link != NULL; TempCurrent = TempCurrent->link)
{
//Check if this node has a lower priority than the current minimum and if so, swap them.
if (TempCurrent->link->base_pri < TempMin->base_pri)
{
TempMin = TempCurrent->link;
TempSwap = TempCurrent;
}
}
//Swap nodes if the head is not the same as the minimum.
if (TempMin != TempHead)
{
swapNodes(&TempHead, TempHead, TempMin, TempSwap);
}
//Recursively sort the rest of the list.
//FOR SOME REASON THE NODE POINTER IS NOT BEING PASSED HERE (EMPTY)
TempHead->link = SortList(TempHead->link, *SortMethod);
return head;
}
void swapNodes(POINTER *head, POINTER CurrentHead, POINTER CurrentMinimum, POINTER TempSwap)
{
//Set new head as the minimum.
*head = CurrentMinimum;
//Link the current temp swap to the head.
TempSwap->link = CurrentHead;
//Swap pointers.
POINTER temp = CurrentMinimum->link;
CurrentMinimum->link = CurrentHead->link;
CurrentHead->link = temp;
}
I am not sure why it is isn't being passed back into the same function, when I debug the linked list seems okay. I suspect that I am missing something in the swap node function, but I don't quite understand what this is. Can someone please offer some insight as to how this code should go for swapping the nodes around?
If you need additional information please let me know.

SortList(TempHead->link, *SortMethod); needs to be listed as SortList(&TempHead, *SortMethod);
To correctly pass the pointer.

Related

C: Function do Delete a Node on a Generic Linked List

I'm having trouble writing a function that will delete a node on a generic linked list.
I have my linked list declared as follow (this is the way my professor wants us to do):
typedef enum _STATUS {ERROR,OK} STATUS;
typedef enum _BOOLEAN {FALSE, TRUE} BOOLEAN;
#define MAX_NOME 20
typedef struct _FUNC
{
char name[MAX_NOME];
char dept[MAX_NOME];
BOOLEAN permanent;
} FUNC;
typedef struct _LIST_NODE
{
void * data;
struct _LIST_NODE * next;
} LIST_NODE;
typedef LIST_NODE * LIST;
#define DATA(node) ((node)->data)
#define NEXT(node) ((node)->next)
I've come with this function to delete all nodes with permanent == FALSE, but it is really not working.
void DeleteFuncNotPermanent(LIST *list)
{
LIST *node = list;
while ((*list)->next != NULL)
{
if(((FUNC*)DATA(*list))->permament == FALSE)
{
node = list;
list = &(NEXT(*node));
free(DATA(*node));
free(*node);
}
else
{
list = NEXT(*list);
}
}
}
Any feedback would be greatly appreciated. Thank you.
You iterate through the list with a pointer to node pointer, which is a good idea. (Typecasting away the pointer nature of LIST is not a good idea, however.) There are several errors in yur code.
To get a pointer to the last element of a list, you do:
Node **p = &head;
while (*p) {
p = &(*p)->next;
}
Your respective code, i.e. your function without the deletion stuff, looks like this:
while ((*list)->next != NULL) {
list = NEXT(*list);
}
You should iterate while (*list). The idea to check next probably stems from similar code that uses a node pointer to iterate. When you use a pointer to node pointer, dereferencing that pointer has the same effect as accessing next, because that pointer points to the head node at first and to the previous node's next member on subsequent iterations.
That's why you must assign the address of (*list)->next to list when you want to advance the pointer. (The comiler warns you that the pointer types don't match.)
So the "raw" loop should be:
while (*list != NULL) {
list = &NEXT(*list);
}
Now let's look at deletion. When you have determined that the node should be deleted, you do:
LIST *node = list;
list = &(NEXT(*node));
free(DATA(*node));
free(*node);
Here, you do not want to advance the iterator pointer. Instead, you want to update what it points at: You want to skip the current node *list by deflecting the pointer that points to the node to the next node or to NULL, when that was the last node:
*list = NEXT(*node);
When you do that, list and node will still be the same address, only the contents have changed. (Because node == list, *node now points at the node after the node you want to delete and you accidentally free that node and its data. Make the temporary pointer a simple node pointer:
LIST node = *list;
*list = NEXT(node);
free(DATA(node));
free(node);
Putting it all together:
void DeleteFuncNotPermanent(LIST *list, int c)
{
while (*list)
{
if (((FUNC*) DATA(*list))->permament == FALSE)
{
LIST node = *list;
*list = (NEXT(*list));
free(DATA(node));
free(node);
}
else
{
list = &NEXT(*list);
}
}
}
I have tried to rewrite your delete function. Let me know if it works. The changes are basically related to pointer dereferencing.
void DeleteFuncNotPermanent(LIST *list)
{
LIST *node = list;
while (list!= NULL) // change
{
if(((FUNC*)(DATA(list)))->permament == FALSE) // change
{
node = list;
list = NEXT(node); //change
free((FUNC*)(DATA(node))); // change
free(node); // change
}
else
{
list = NEXT(list); //change
}
}
}

C insert element into ascending linked list

I have problems with my insert method, as for some reason I end up with infinite loop. Here is my struct:
struct List {
char element;
struct List *next;
};
And here is my insert method:
void insert(struct List *first, char el){
struct List *new=NULL;
struct List *current = first;
new = (struct List*) malloc (sizeof(struct List));
new->element = el;
new->next = NULL;
if (first == NULL){
first = new;
return;
}
while (1){ //this loop never ends
if (current->next == NULL) break;
if (current->next->element < el){
current = current->next;
}else{
break;
}
}
struct List *ex_next = current->next;
current->next = new;
new->next = ex_next;
}
I am aware of similar question here: C - Inserting into linked list in ascending order but it didn't really help me.
The first argument to insert is a pointer. But you need a pointer to a pointer (struct List **first).
If the list is empty, you pass the VALUE NULL into the function (the variable first inside the method have the value NULL). Then you assign a new malloced value to it and return. The variable on the calling side haven't changed and your memory leaked.
When you pass a pointer of a pointer, the variable first holds the address of the variable of the calling method. This way, you can reassign it's value.
Pointers, Pointers of Pointers, Pointers of Pointers of arrays of functions returning function pointers .... thats the fun part of C ;)

Bubble-sorting doubly linked list

I have a problem with my bubble-sorting function for the doubly linked list.
It is working when I'm sorting the nodes in the singly linked way (only with ->next), but I can't make it work with ->prev pointers.
Here is the code I'm using:
void sort(int count)
{
struct data *tmp,*current,*nextone;
int i,j;
for(i=0;i<count;i++)
{
current = first;
for(j=0;j<count-1-i;j++ )
{
if(current->number > current->next->number)
{
nextone = current->next;
current->next = nextone->next;
nextone->next = current;
if(current == first)
{
first = nextone;
current = nextone;
}
else
{
current = nextone;
tmp->next = nextone;
}
}
tmp = current;
current = current->next;
}
}
}
And this is the structure I'm using (with the global variables for the first and last element of the list):
struct data
{
int id;
char name[20];
int number;
struct data *next;
struct data *prev;
};
struct data *first = NULL;
struct data *last = NULL;
Below logic would work.
I would follow similar algorithm... If you want to move the entire nodes...
struct data *before, *after;
if(current->number > current->next->number)
{
before = current->prev;
after = current->next;
if(before != NULL){
before->next = after;
}
current->next = after->next;
current->prev = after;
after->next = current;
after->previous = before;
}
Alternatively, you can simply swap the numbers in the nodes without bothering to move entire nodes, if sorting of the data is the purpose. You can expand the below logic to include swapping of both char array and id as well.
if(current->number > current->next->number)
{
int tempNum = current->number;
current->number = current->next->number;
current->next->number = tempNum;
}
You just need to sit down and think about it for a bit.
Assigning first? Well the previous must be null.
Assigning last? Next must be null.
Anything else you need to do a little swap-dance with previous & next of the elements you are swapping.
A much easier way (that will quickly become faster too for larger array sizes) is to allocate an array of pointers, fill those with pointers to the elements, qsort the array then do another pass to re-wire the pointers (and delete the temp array).
One easier way is just to copy the data of the node, you can swap the data but the pointer to the node.
if(current->number > current->next->number){
swap(current->id,current->next->id);
swap(current->name,current->next->name);
swap(current->number,current->next->number);
}
With your method, you never set the previous pointer at all. You can sort the double linked list as single list at first, and then traversal the list again to set all the prev pointer.
Of course, you can sort the double linked list at a time, but it's a little complex to implement. You should consider 4 nodes each step, i.e current, current->next, as well as current->prev and current->next->next. When you want to swap current and current->next,
you should set current->prev->next=current->next, current->next=current->next->next so on and so forth.

Doubly linked lists in C

#include<stdio.h>
typedef struct Node
{
int data;
struct Node *next;
struct Node *prev;
} node;
void insert(node *pointer, int data)
{
while(pointer->next!=NULL)
{
pointer = pointer -> next;
}
pointer->next = (node *)malloc(sizeof(node));
(pointer->next)->prev = pointer;
pointer = pointer->next;
pointer->data = data;
pointer->next = NULL;
}
int main()
{
node *start;
start = (node *)malloc(sizeof(node));
int data;
scanf("%d",&data);
insert(start,data);
}
Well, I'm trying to understand the basics of lists in C. I have one question here - the 3rd line from the insert()'s bottom - what is this for? It seems like if the first list's element remains empty and the data is being saved into the second one. But only this works.
In main() there's being created first empty element, right?
while() is not executed as the element is null.
Then the second element is being created. (pointer->null)
Pointer pointing to the first element is set to point to the second element (that 3rd line from the bottom)
And the data is being saved to that second element.
Where do I make a mistake?
pointer = pointer->next;
This line changes the current node we're concentrating on from the last node of the original list to the newly allocated last node of the new list (i.e. the node after the last node of the original list). We then set that node's values directly in the next two lines.
You could get rid of this line and change the two lines below it to read
pointer->next->data = data;
pointer->next->next = NULL;
and you would have the same result.
Edit: Looking further into things, I see more issues:
1) You need #include <stdlib.h> to use malloc().
2) You need to explicitly set start->next = NULL; before you call insert().
In your insert function, you have this assignment:
pointer = pointer->next;
This will not work, as the pointer pointer is passed by value. This means that all changes to the value will be lost when the function returns.
You can pass the pointer by reference:
void insert(node **pointer, int data)
{
/* ... */
*pointer = (*pointer)->next;
/* ... */
}
Or return the pointer from the function:
node *insert(node *pointer, int data)
{
/* ... */
return pointer;
}
There is also the problem that you don't initialize the pointers in the node structure. This means that when you allocate a node structure, the fields in it will be pointing to seemingly random locations.
This is solved simply by setting the next and prev pointer to NULL directly after allocation:
start = malloc(sizeof(node));
start->next = NULL;
start->prev = NULL;

Maintaining chain of pointers to address

This is something of a followup to a question I asked earlier. I'm still learning my way around pointers, and I'm finding it difficult to maintain a reference to the physical address of a struct while iterating through a data structure. For example, I have a simple, barebones linked list that I'd like to delete from via a searching pointer:
struct Node{
int value;
struct Node* next;
};
struct Node* createNode(int value){
struct Node* newNode = malloc(sizeof *newNode);
newNode->value = value;
newNode->next = NULL;
return newNode;
}
void nodeDelete(Node **killptr){
free(*killptr);
*killptr = NULL;
}
int main(){
struct Node* head = createNode(16);
head->next = createNode(25);
head->next->next = createNode(51);
head->next->next->next = createNode(5);
// Working code to delete a specific node with direct reference address
struct Node** killptr = &head->next;
nodeDelete(killptr);
return 0;
}
The above shows deleting by passing nodeDelete a pointer to the address of the head pointer. What I want to do is be able to move my pointer ->next until it finds something that satisfies a delete condition, and call nodeDelete on that. I've tried the following:
struct Node* searchAndDestroy = head;
while(searchAndDestroy->value != NULL){ // Search until the end of the structure
if (searchAndDestroy->value == 25){ // If the value == 25
nodeDelete(&searchAndDestroy); // Delete the node (FAILS: Nullifies the
// address of search variable, not the
break; // original node)
}else{
searchAndDestroy = searchAndDestroy->next;
}
}
I've also tried something along the lines of:
if (searchAndDestroy->value == 25){
struct Node** killptr = (Node**)searchAndDestroy);
nodeDelete(killptr); // Still fails
}
I need to be able to move my pointer to the ->next point, but also maintain a reference to the address of the node I want to delete (instead of a reference to the address of the search node itself).
EDIT: Some clarification: I realize that deleting from a linked list in this fashion is naive, leaks memory, and drops half the list improperly. The point is not to actually delete from a linked list. Ultimately the idea is to use it to delete the leaves of a binary search tree recursively. I just figured a linked list would be shorter to portray in the question as an example.
struct Node **searchAndDestroy;
for (searchAndDestroy = &head;*searchAndDestroy; searchAndDestroy = &(*searchAndDestroy)->next ){
if ((*searchAndDestroy)->value == 25){
nodeDelete(searchAndDestroy); // Function should be changed to assign the ->next pointer to the **pointer
break;
}
}
And change nodeDelete like this:
void nodeDelete(Node **killptr){
Node *sav;
if (!*killptr) return;
sav = (*killptr)->next;
free(*killptr);
*killptr = sav;
}
Unless I'm missing something, your nodeDelete function is working as designed, but you want to keep a way of accessing the next node in the chain. The easiest way of doing this is just to add a temporary variable:
struct Node *searchAndDestroy = head, *temp = NULL;
while(searchAndDestroy != NULL){ // Need to check if the node itself is null before
// dereferencing it to find 'value'
temp = searchAndDestroy->next;
if (searchAndDestroy->value == 25){
nodeDelete(&searchAndDestroy);
break;
}else{
searchAndDestroy = temp;
}
}
if you give the Address of the previous Node that is where the link to deleting node present then it is very simple
code snippet for that:-
void delete_direct (struct Node *prevNode)
{/*delete node but restrict this function to modify head .So except first node use this function*/
struct Node *temp;/*used for free the deleted memory*/
temp=prevNode->link;
prevNode->link=temp->link;
free(temp);
}
struct Node * find_prev(struct Node *trv_ptr,int ele)
{
/*if deleting element found at first node spl operation must be done*/
if(trv_ptr->data==ele)
return trv_ptr;
while((trv_ptr->link)&&(trv_ptr->link->data!=ele))
{
trv_ptr=trv_ptr->link;
}
if(trv_ptr->link==NULL)
{
return NULL;
}
else
return trv_ptr;
}
main()
{
/*finding Node by providing data*/
struct Node *d_link;
struct Node *temp;
d_link=find_prev(head,51);
if(d_link==NULL)
{//data ele not present in your list
printf("\nNOT FOUND\n");
}
else if(d_link==head)
{//found at first node so head is going to change
temp=head;
head=head->link;
free(temp)
}
else
{//other wise found in some where else so pass to function
delete_direct (d_link);
}
}

Resources