I wrote this function in order to swap 2 nodes in a linked list, but the result is a Segmentation Fault. Can you check it? Thanks.
(I did typedef for struct student* as punt)
void swap_node(punt node1, punt node2)
{
node1->next=node2->next;
node2->next=node1;
node2->prev=node1->prev;
node1->prev=node2;
(node2->prev)->next=node2;
}
I guess this would be sufficient, basically the only difference from your code is the last statement (did not include null checks for simplicity):
void swap_node(punt node1, punt node2)
{
node1->next=node2->next;
node2->next=node1;
(node1->prev)->next=node2;
node2->prev=node1->prev;
node1->prev=node2;
(node1->next)->prev=node1;
}
When possible, two nodes are more easily swapped by only swapping their contents. The result is identical. This would pose a problem if somewhere else in your code you held a pointer to one of the nodes you swapped and are still expecting it to be the same node.
Anyway, back to your code. There are a few things that need refinement. Firstly, we need to check if node1 and node2 are not null and if they aren't we can proceed, otherwise exit the function as we can't swap this. The first 4 lines of your swapping code are alright, but here's two issues:
Node that was before node1 (prior to swapping) might be null, and thus accessing it's next pointer to point at node2 would result in undefined behaviour and likely crash. Check if it exists and then perform the assignment.
You forgot to check if there exists a node after node2 (prior to swapping) and if so, point it's prev pointer at node1
It usually helps to visualize the links in a doubly linked list, just draw it on a piece of paper. You'll notice 4 links per node:
node->next
node->next->prev
node->prev
node->prev->next
So to swap two unrelated nodes you need to swap the 4 links.
The following will not work:
node1->next=node2->next;
This overwrites the old value node1-> next. You need to use a temporary variable instead. Something like this:
#define SWAP_PTR(a,b) { void* tmp = b; b = a; a = tmp; }
void swap_node(punt node1, punt node2)
{
SWAP_PTR(node1->next, node2->next);
SWAP_PTR(node1->next->prev, node2->next->prev);
SWAP_PTR(node1->prev, node2->prev);
SWAP_PTR(node1->prev->next, node2->prev->next);
}
Note - in a threaded environment you'll need some form of locking.
Related
This is done by someone who is better in C programming than me.
confused over the usage of free () in C
below is the struct of linked list
typedef struct node {
int value;
struct node *next;
} ListNode;
typedef struct list {
ListNode *head;
} LinkedList;
after created a list with some nodes
his code does this when exit
void deleteList(LinkedList *ll) {
if (ll != NULL) {
ListNode *temp;
while (ll->head) {
temp = ll->head;
ll->head = temp->next;
free(temp);
}
free(ll);
}
}
The above is what I don't understand. Why he needs to create such complexity, why not just do free(ll).
Please help
thanks in advance.
Linked list is made up of individual objects that happen to point at each other. If you want to delete a list you have to delete all of its nodes. free() won't do that. It doesn't know that these objects make up a list. It doesn't even know that these objects contain pointers to anything. Therefore you need to iterate over the list and free each node by hand.
if you have a linked list, suposse that every "*" is a node.
0 1 2 3 4
head--> *--*--*--*--*
the first *, es the head, is you just do "free ll"
this will be on the memory
0 1 2 3 4
head-->nul *--*--*--*
the problem here, is, all the "memory" that you ask for those nodes will still be there, and now you can't know where is it (you have nothing poiting to that memory) for every malloc you need a free (not 100% true, but for simple things work).
What that algorithm do is:
get the reference to the next node (if you don't do this and you free the node, you won't be able to get the "next" node, becouse head will be pointing to nothing ).
free the head.
make head point to the reference that you get before.
You can do with the following struct alone
typedef struct node {
int value;
struct node *next;
} ListNode;
But every time you have to declare a global variable struct node *HEAD. In a bigger program it may confuse you. The author has done this so that you can create a linked list like a variable. Every time you have to create a new linked list all you have to do is declare
LinkedList *ll;
When there are two struct, one has to free the objects of both the struct.
That's because each pointer points to a memory location. You need to free all memory locations that were previously allocated. free(ll) would only remove the node pointed to by the ll pointer.
Good day guys, im new here to C and am trying to learn linked lists. I been trying to swap 2 nodes from within a linked list but so far have been having trouble getting it to work. The code I been trying to use causes an endless circular loop, but I don't think it is because of the if or while statement.
Take a look? Any pointers here? Help would be greatly appreciated.
Basically, the code uses a user input to search for a node based on the data inside, then it should swap the node with the data inside with the next node. Been at this for 3 hours, can anybody help? Thanks!
/conductor is the name im using of the pointer for the current node/
#include <stdio.h>
#include <stdlib.h>
struct node {
int x;
struct node *next;
struct node *prev;
};
struct node *root;
struct node *conductor;
struct node *counter;
struct node *newnode;
struct node *back;
struct node *swapper;
struct node *swappee;
struct node *blanker;
int add = 0;
int initialization = 0;
int query = 0;
int swap ()
{
printf("enter data to search from within the nodes: ");
fflush(stdin);
scanf("%d", &query);
conductor = root;
while ( conductor->next != 0)
{
if(conductor->x == query)
{
printf("\n%d\n", query);
swapper = conductor;
swappee = conductor->prev;
conductor = swappee;
conductor->next = swapper;
break;
}
else
{
conductor = conductor->next;
}
}
mainMenu ();
}
A double linked list (like the one you have) is basically an array of node, each node pointing to its neighbors. Let's say we have nodes -A-B-C-D- (A-B means that A points to B and B points to A). Let's say you want to swap B and C. You have to make 4 changes:
Make A point to C
Make C point to B and A
Make B point to D and B
make D point to B
You make only the second and the third change. So, you need to add A->next = B and D->prev=C. I hope it is clear enough.
Also, you should not fflush input streams.
If you want to swap the data:
if (conductor->x == query) {
int temp = conductor->x;
if (conductor->next)
conductor->x = conductor->next->x;
conductor->next->x = temp;
}
}
Typically that is what you will want to do. If you have a structure with several members instead of the 1 int, swapping the pointers may seem less messy in theory, but it isn't, primarily due to the fact that you must test for existence of a next/previous node so often. In truth, you'd probably want a pointer to a separate structure in such a case.
Given three nodes — previous, current, and next, pointing to current->prev, current, and current->next respectively — you must update at most 6 pointers:
next->prev = previous
previous->next = next
current->prev = next
current->next = next->next
next->next = current
current->next->prev = current
Step 2 is not necessary if previous is NULL.
Step 7 is unnecessary if current->next is NULL.
The entire thing is unnecessary if next is NULL.
If you want to swap with the previous node instead of the next, exchange any instance of the variable previous with the variable next and vice-versa as well as exchanging any instance of ->prev with ->next and vice-versa.
Overall, this requires a fair bit of branching code, which can be slow. This is why it is usually better to swap the data rather than messing with the pointers. It gets even messier when you want to swap with the previous node and you only have a singly-linked list that points to the next node because you must store yet another pointer for the equivalent of previous->prev, assuming previous exists.
I have to print a list of a set in c using linked lists (hence pointers). However,when I delete the first element of the list and try to print the list, it just displays a lot of addresses under each other. Any suggestions of what the problem might be? Thanks!
Delete function:
int delete(set_element* src, int elem){
if (src==NULL) {
fputs("The list is empty.\n", stderr);
}
set_element* currElement;
set_element* prevElement=NULL;
for (currElement=src; currElement!=NULL; prevElement=currElement, currElement=currElement->next) {
if(currElement->value==elem) {
if(prevElement==NULL){
printf("Head is deleted\n");
if(currElement->next!=NULL){
*src = *currElement->next;
} else {
destroy(currElement);
}
} else {
prevElement->next = currElement->next;
}
// free(currElement);
break;
}
}
return 1;
}
void print(set_element* start)
{
set_element *pt = start;
while(pt != NULL)
{
printf("%d, ",pt->value);
pt = pt->next;
}
}
If the list pointer is the same as a pointer to the first element then the list pointer is no longer valid when you free the first element.
There are two solutions to this problem:
Let all your list methods take a pointer to the list so they can update it when neccesary. The problem with this approach is that if you have a copy of the pointer in another variable then that pointer gets invalidated too.
Don't let your list pointer point to the first element. Let it point to a pointer to the first element.
Sample Code:'
typedef struct node_struct {
node_struct *next;
void *data;
} Node;
typedef struct {
Node *first;
} List;
This normally happens when you delete a pointer (actually a piece of the memory) which doesn't belong to you. Double-check your function to make sure you're not freeing the same pointer you already freed, or freeing a pointer you didn't create with "malloc".
Warning: This answer contains inferred code.
A typical linked list in C looks a little something like this:
typedef struct _list List;
typedef struct _list_node ListNode;
struct _list {
ListNode *head;
}
struct _list_node {
void *payload;
ListNode *next;
}
In order to correctly delete the first element from the list, the following sequence needs to take place:
List *aList; // contains a list
if (aList->head)
ListNode *newHead = aList->head->next;
delete_payload(aList->head->payload); // Depending on what the payload actually is
free(aList->head);
aList->head = newHead;
The order of operations here is significant! Attempting to move the head without first freeing the old value will lead to a memory leak; and freeing the old head without first obtaining the correct value for the new head creates undefined behaviour.
Addendum: Occasionally, the _list portion of the above code will be omitted altogether, leaving lists and list nodes as the same thing; but from the symptoms you are describing, I'm guessing this is probably not the case here.
In such a situation, however, the steps remain, essentially, the same, but without the aList-> bit.
Edit:
Now that I see your code, I can give you a more complete answer.
One of the key problems in your code is that it's all over the place. There is, however, one line in here that's particularly bad:
*src = *currElement->next;
This does not work, and is what is causing your crash.
In your case, the solution is either to wrap the linked list in some manner of container, like the struct _list construct above; or to rework your existing code to accept a pointer to a pointer to a set element, so that you can pass pointers to set elements (which is what you want to do) back.
In terms of performance, the two solutions are likely as close so as makes no odds, but using a wrapping list structure helps communicate intent. It also helps prevent other pointers to the list from becoming garbled as a result of head deletions, so there's that.
I've frequently used a linear linked list construct in C
typedef struct _node {
...node guts...
struct _node *next
} node;
And the enumeration idiom
for (node *each = headNode; each != NULL; each = each->next)
I'm in a situation right now, where a circular list is attractive to me (e.g. the next of the last node is set to the headNode). Naively, I thought I'd using something similar to the for expression there, and the more I've stared at it, I think I've convinced myself you can't do something like that with a circular linked list.
It seems that no matter what kind of expression I come up with for the end conditional, I'll have the basic problem that I want said conditional to evaluate true the first time and false the second time the same node is encountered. I could do something with a loop side effect:
for (BOOL traversed = FALSE, node *each = headNode;
traversed && each != headNode;
traversed = TRUE, each = each->next)
But that definitely loses the elegance/simplicity of the null terminated list approach. Is there some trick of logic that's eluding me this late in the day?
Obviously I can use a while() construct, and maybe that's the only way to do it.
Assuming the following:
An empty list means NULL for the starting point.
The last node will be the one who's next pointer references your starting point, including a single-node list who's next pointer self-references.
NO next pointers are NULL.
Then the following will do what you want using a tertiary expression as the incremental step.
// node* start comes from "somewhere'
for (node *p=start; p; p = (p->next==start ? NULL : p->next))
{
// do something with p
}
Note: p will be NULL when this exits, one way or another; start will remain unchanged, and a NULL start is acceptable, as is a single-self-referencing node.
That said, I'd do this with a while-loop, but since you specifically asked for a for-loop you gets that =P.
As you've already pointed out, any "current" pointer will simply cycle through the whole list, being no different on its second time around than the first.
Therefore you must use some other piece of information. As the simplest/smallest additional variable, a boolean, is too complex or inelegant by your admission, it can be inferred that this can't be done.
...unless you're already making use of some additional data, say a "prev" pointer that's initially null (in which case something very similar to your traversed example can be used).
For the record, I'd use a do...while:
struct _node *node = head;
do {
...
node = node->next;
} while (node != head);
I suggest not just to loop a chain. It is not beautiful itself. How do you check you don't loop at the second element of the chain and so on?
If we have a notion of a head then we should hold a pointer to a head in each node or have it static. Also, instead of looping to the head, we should have special flag.
p = (pointer to some node)
pfirst = p
while true:
do whatever with node (p)...
p = p->next
if (p==pfirst) break
Note that this works for a one element list.
The following re-arranged loop doesn't look too terrible:
for (node * n = head; ; )
{
// process n
if ((n = n->next) == head) { break; }
}
By the way, this only works for non-empty lists. Whatever your notion of emptiness is should be added as an initial check — unless you avoid that problem by having dummy head node (so that the empty list contains one node n with n == head == n.next, and in that case, you don't actually want to print the dummy at all and can use a genuine for loop (just put the above condition back into the loop statement)!
I was reading about a few basic operations on linked list and I saw two types of loops being used predominantly
struct node {
int data;
struct node *next;
}*start=NULL,*tmp;
The first loop was of the form
for(tmp=start;tmp->next!=NULL;tmp=tmp->next);
Using the above loop, now the tmp pointer points towards the last node in the list
The second loop was of the form
tmp=start;
while(tmp!=NULL)
{
// do something
}
I think that both of them do the same work, but I'm not sure. Is there any difference?
I suppose your while loop is something like this.
temp=start;
while(temp!=NULL)
{
// do something
temp= temp->next;
}
In your code of for loop, When you are out of the for loop, temp is not pointing to NULL. temp is pointing to end of the linked list. But in case of while loop, your temp is pointing to NULL after you exit the while loop and you don't have tail(Unless you assign temp to any other temporary variable to change the logic of program) with you if you want to use it in the further steps. That is the only difference. Except that there isn't much difference.
You could have checked it by writing a small program and printing the results. I recommend you do it.
The loops aren't identical. In fact, your for loop has a problem. Consider what happens when start==NULL before you enter the for loop.
for(tmp=start;tmp->next!=NULL;tmp=tmp->next);
You assign start to tmp and then dereference tmp, a NULL pointer. I think you want the following instead.
for(tmp=start;tmp!=NULL;tmp=tmp->next);
That change makes the for and while loops the same.
Q: Effectively, "no". There isn't any substantive difference; they both do the same work.
You can always code a "for()" loop with an equivalent "while()".
I use while loop when I need to change the linked list. For e.g.
while (root->next)
{
if(0 == strcmp(root->data,root->next->data))
{
temp = root;
root = root->next;
free(temp)
}
else
{
root = root->next;
}
}
I use for loop when I need a read only access to linked list.