I have a list inside a structure,
struct A{
list B;
};
I also have a pointer to this structure like
struct A *a;
Now, assume the list is already implemented, and its elements are of type elem.
Then, I do the following -
(i) I get the head of the list in nodeL (a pointer of elem type)
elem * nodeL = list_get_head(&(a->B));
(ii) I now loop through the list in this way:
while(nodeL != (elem *)(&a->B)){ // did not get this part ----- I
; //some code here
nodeL = list_get_next(&(a->B), nodeL);
}
Assume that list_get_head gets the pointer to head of list, and list_get_next gets the pointer to next element of passed second argument elem, of list.
Now my question is:
What is my loop condition here? Till what of list do I hope to loop? (see I) In other words, if &(a->B) is the address of the start of the list, what is &a->B here?
I thought this should loop till end of list, but it doesn't seem like that's what the while loop condition is doing. Also, this is a circular doubly linked list.
elem* x = list_get_head(&a->B);
elem* y = (elem *)(&a->B);
First at all, how far do x and y differ in your case?
To be valid at all, first member of list must be of type elem* anyway. I personally would assume that this is the head of the list, but then your while loop won't ever be entered, so it must rather be the tail??? But then your first element considered in the loop is the tail...
How is an empty list represented? Null pointer? If so, this is not covered by your code.
while(nodeL != (elem *)(&a->B))
did not get this part
Idea is simple: we start iterating at the head, and as long as the head is not reached again, we are still in the loop... Problem is, though, you have to distinguish two situations:
current node is head at start of the loop
current node is head after all elements have been iterated
I recommend different handling for iterating now:
elem* nodeL = list_get_head(&a->B);
if(nodeL) // based on assumption(!): otherwise, empty list
{
do
{
//some code here
nodeL = list_get_next(&a->B, nodeL);
}
while(nodeL != list_get_head(&a->B));
}
One element is guaranteed to be in the list, so we can use it unconditionally (thus a do-while loop). Then we iterate to the next element, until we reach the beginning again. I replaced the suspicious cast by another call to list_get_head, making the whole matter safer (not relying on assumptions any more either).
It is not clear if you are talking about std::list. But since you are mentioning unfamiliar list_get_head and list_get_next, I assume this is a non standard implementation.
So the guess is: In a list head is also an element. The condition while(nodeL != (elem *)(&A->B)) looks like it is checking when loop traverses back to the head of the circular list.
However, there are two observations:
Elements of a list are generally allocated from heap or a memory pool. Hence, if B is head, it should have been a pointer. Unless it is typedef elem * list.
The loop may not run because nodeL is already at head.
Suppose you have a variable and a pointer of the structure:
A var;
A* pntr;
Now to access a data member of the structure we do the following :
var.B // For a variable use dot
(*pntr).B or pntr->B //For a pointer we can use * . or ->
In your code while(nodeL != (elem *)(&a->B)) iterates over the list till the head of the circular list is encountered again.
We can get head by :
A* a;
//SOME INITIAL CODE
A* pntr;
pntr = a;
head = (elem*)&(pntr->B)
So look at the look like:
while( nodeL != (elem *) ( & ( a->B ) ) )
Related
I am talking about deletion in a linked list, source is from The Algorithm Design Manual 3rd ed - Steven Skiena. These are the demo files of the code:
https://www.algorist.com/adm-code/list-demo.c
https://www.algorist.com/adm-code/list.h
Considering the following code, and assuming that here uses pointer to pointer to "simulate" a kind of reference call, we have:
list *item_ahead(list *l, list *x) {
if ((l == NULL) || (l->next == NULL)) {
return(NULL);
}
if ((l->next) == x) {
return(l);
} else {
return(item_ahead(l->next, x));
}
}
void delete_list(list **l, list **x) {
list *p; /* item pointer */
list *pred; /* predecessor pointer */
p = *l;
pred = item_ahead(*l, *x);
if (pred == NULL) { /* splice out of list */
*l = p->next;
} else {
pred->next = (*x)->next;
}
free(*x); /* free memory used by node */
}
Doing some opening clarifications about some parts:
Considering the function item_ahead(), in the following:
if ((l == NULL) || (l->next == NULL)) {
return(NULL);
}
the condition at the left of OR :
is true when the list is empty, i.e. when there aren't element of type list to point for;
the condition at the right of OR :
is true when it comes at the last element of the list without have found the x you look for.
===
Looking at this portion of code:
if ((l->next) == x) {
return(l);
} else {
return(item_ahead(l->next, x));
}
in every recursive call, *l assumes always the address of the next list element;
so, at last, when the element is found or not, the pointer l points to the element wanted or to a null (because the element is not found, or the list is empty), (however, doesn't point anymore to the head).
===
Considering now the function delete_list():
pred = item_ahead(*l, *x);
after the return, we have that *l and pred points to the same thing (element or null).
===
So, after all these premises, this is the main part I don't understand:
if (pred == NULL) { /* splice out of list */
*l = p->next;
} ...
Now, pred == NULL, for all the implications said above, and :
Why, we have that, after the assign, *l points to the next element, i.e. to the second element of the list?
In my opinion, since *l still points to the element wanted or is a null pointer, we would want to "reset" it to points to the head of the list, so, I think that it would be written instead as *l = p.
And also, I have the following questions:
What is the meaning of the phrase in the code: "splice out of list"?
And why there is the need to "reset" *l, since it is not used anymore?
Please, can you help me? Many thanks!
I was reading, and I found this:
void insert_list(list **l, item_type x) {
list *p; /* temporary pointer */
p = malloc(sizeof(list));
p->item = x;
p->next = *l;
*l = p;
}
Let's see, *l will be always the last node added. p->next will save the predecessor to *l.
With that premise we can answer to your first question:
Why, we have that, after the assign, *l points to the next element, i.e. to the second element of the list?
Because the current node is the target, that is, the last inserted node. After the assign, *l points to the preceding list. Also remember, this linked-list is in descending order (the last node is the head).
In my opinion, since *l still points to the element wanted or is a null pointer, we would want to "reset" it to points to the head of the list, so, I think that it would be written instead as *l = p.
You do that when you're working in ascending order (the first node inserted is the head). But in this case, *l is x and it does mean to get rid of x you just need to set head to the preceding list (p->next).
What is the meaning of the phrase in the code: "splice out of list"?
The target to delete (x) doesn't have an ahead item (x is the last inserted node) so you don't need to do pred->next = (*x)->next. Then your out of list (no ahead item to do that).
And why there is the need to "reset" *l, since it is not used anymore?
Not used anymore? It is used! *l saves your last added node. From there you can go through the entire list. And you reset it to the preceding list (p->next) because the current node is the target to delete.
I think this' a good reference: linked-list-good-taste
This' my first answer here. I hope I've helped you :D.
pred = item_ahead(*l, *x)
after the return, we have that *l and pred points to the same thing (element or null).
pred and *l don't point to the same thing, *l still point to the head
As you can see in list-demo.c, Skiena first checks whether the element is present in list, and he calls delete_list only if he already knows that the element is present.
You actually have just one case: item_ahead could return NULL only if the element is present and in first position.
In the recursive calls inside item_ahead, l stays the same. He just recursively calls item_ahead passing l's next pointer, and then the pointer next to l's next pointer, and so on.
After pred = item_ahead(*l, *x); inside delete_item,
*l and pred don't point to the same "thing".
In fact, he passed the pointer to item_ahead by value (it's C, after all). Even if he had made changes to the pointer itself internally to the function, and he had not, they would not have been maintained externally. If you want to change the pointer itself, you must pass a pointer to a pointer in C, as in delete_item. If you pass a pointer by value, you could only change the value the pointer point to, but he does not even make any change of this kind.
He can change the actual pointer *l inside delete_item, since it passes a pointer to a pointer. In this function, the instruction
*l = p->next; splices out the first element. It means that it cuts off the first element and replaces it with his next one.
I hope I was clear, it is my first answer on stackoverflow.
So i'm writing this function that makes the smallest node at the end of the linked list. So Far it works except for the last element. if the linked list is created as nodes containing items from 0~9, the list after this function is {1,2,3,4,5,6,7,8,0,9} and the zero does not become at the end.
Any ideas why?
my function;
int connecting(linkk A)
{
linkk L = A;
for (int i = 0; i<sizeof(L);i++)
{
if (L->item < L->next->item)
{
int a = L->item;
L->item = L->next->item;
L->next->item = a;
L = L->next;
}
else{L=L->next;}
}
return 0;
}
Let's start with what I think you should be doing differently:
The name of your function is connecting. Given the description of what you'd like the function to do, this is not a good name.
I can infer from the usage that linkk is a typedef'ed pointer. Hiding pointers this way is not a good idea, most of the time.
Your function returns an int. It's always 0. Why? This does not make any sense.
Because linkk is probably a pointer to a node, and you pass the head pointer by value (i.e. a copy of it) to the function, you're unable to handle the case where the head of your list is the minimum. Either return the "new" head pointer, or pass a pointer to the head pointer to be able to modify the head pointer.
Your use of sizeof is completely wrong, as already suggested. sizeof(L) will give you the size (in chars) of the pointer, thus probably 8 on a 64 bit system or 4 on a 32 bit system.
You're not changing the nodes, but rather moving the values in between the nodes. This is OK if it's what you want to do, but the description of your algorithm suggest that you want to move the node instead.
Your function is doing too much, IMO. This can be hard to split, but a better approach would be to separate finding / extracting the minimum and putting it at the end of the list.
You're modifying a lot more than you originally wanted to. Consider a list like 1, 2, 3, 0, 4. Using your algorithm, the list will be changed to 2, 3, 1, 4, 0. Doing this is not only bad for the performance, but also very surprising for the caller. Surprises aren't good when it comes to programming!
So, let's get to a hopefully good implementation, step by step:
struct node {
int item;
struct node * next;
};
I assume that you want to move the node containing the minimum value to the end of the list, as in your description. I'm also going to keep this into a single function receiving a struct node * head pointer, despite my point above, in order to keep closer to the original code. Let's get out the special / base cases first: Moving the minimum element of an empty list as well as of a single element list is trivial: Do nothing.
if (head == NULL || head->next == NULL) {
return head;
}
I'm returning the "new" head of the list to allow the caller to update it's own head pointer. (As already said, head is just a copy of the caller's head pointer, modifying it will not have any effect at the call site).
Because we're dealing with a singly linked list here, and the implementation should not unnecessarily iterate over the list, we should remember the node we've previously visited. Otherwise we couldn't easily extract a node from the list:
struct node * follow, * point;
follow follows directly behind the point.
Initially, we place the point to the second node of the list (we already checked that there are at least 2 nodes in the list). follow will thus point to the head:
point = head->next;
follow = head;
Since we want to find the minimum item, we need to keep track of the minimum of the already searched part of the list. We initialize it with the head node's value:
int currentMinimum = head->item;
Now we're ready to iterate over the list, in order to find the node containing the minimum. But we need not only find the node containing the minimum, but also the one before it and the one after it, in order to be able to extract it easily. So, 3 pointers:
struct node * predecessor, * minimum, * successor;
As we set the currentMinimum to heads item, we should also set the pointers accordingly:
predecessor = NULL; // Nothing preceding the head
minimum = head;
successor = head->next;
Now let's iterate, moving the point completely over the list, until it falls off at the end:
while (point != NULL) {
// to be continued
follow = point;
point = point->next;
}
// when we're here, follow will point to the last node
In each iteration, we need to check if we found a smaller value than the current minimum, and eventually remember the node containing it:
if (point->item < currentMinimum) {
predecessor = follow;
minimum = point;
successor = point->next;
currentMinimum = point->item;
}
Now, when we get out of the loop, the following state should be reached:
minimum points to the node containing the minimum.
follow points to the last node of the list.
The two above could be the same, which is a special case!
predecessor could still be NULL, which is another special case!
Considering first the special case of minimum = follow: In that case, the minimum is already at the end of the list, so profit! Otherwise, we need to "cut" the node at minimum out of the list and append it to the last node, pointed to by follow:
if (follow != minimum) {
if (predecessor != NULL) {
predecessor->next = successor; // Cut out
minimum->next = NULL; // will be the last node
follow->next = minimum; // append at end
} else {
// to be continued
}
}
As you can see, there's the second special case to consider: If predecessor is still NULL, then no item was smaller than heads item. (Therefore, we could also test for minimum == head) Thus, the first node of the list will be moved to the end. We need to inform the caller about this!
head = head->next; // Second node is now the first one, though this is not all we need to do, see further down!
minimum->next = NULL; // Will be the last node
follow->next = minimum; // Append at end
Since the assignment to head only changed the function parameter (which is a copy of the pointer with which the function has been called), we need to return the (possibly modified!) head pointer, giving the caller the ability to update its own head pointer:
return head;
A caller would thus use this function like so:
struct node * head = get_a_fancy_list();
head = move_minimum_to_end(head); // This is our function being called!
Finally, a thing to consider: As you can see, moving the node (instead of the item) is more complicated. We need to modify at least 2 pointers in order to achieve what we want. In contrast: Moving the item value requires two modifications of item values (and iterating is easier). Moving the node instead of the item thus makes only sense when pointer assignments are faster than item assignments. Since the items are of type int this is not the case here.
Moving the item instead of the node containing the item is considerably easier. First of all, we need to keep track of the minimum (value as well as node):
struct node * minimum;
int currentMinimum;
To iterate, we're again going to use two pointers. It can be done with a single one, but the code is going to be more readable this way:
struct node * point, * follow;
We start off with the same initial state:
minimum = head;
currentMinimum = head->item;
follow = head;
point = head->next;
Iterating is similar to the other implementation, as is the iteration step:
while (point != NULL) {
if (point->item < currentMinimum) {
minimum = point;
currentMinimum = point->item;
}
follow = point;
point = point->next;
}
// follow points to the last node now
Now, doing the same as the previous implementation, we can swap the items of the last node and the node with the minimum:
minimum->item = follow->item;
follow->item = currentMinimum; // contains minimum->item
There's no point in checking for follow != minimum like in the previous approach: You could do it, but swapping the item of a node with its own item won't do any harm. OTOH adding an if will add a branch, and thus a possible performance penalty.
Since we didn't change the list structure (the linking between nodes), there's not much more to consider. We don't even need to inform the caller about a new head, as there will never be any change to it. For style purposes, I'd add it either way:
return head;
Ok, this got a bit long now, but hopefully it's easy to understand!
Try this function
int connecting(linkk A)
{
linkk L = A;
while(L->next!=NULL)
{
if (L->item < L->next->item)
{
int a = L->item;
L->item = L->next->item;
L->next->item = a;
}
L=L->next;
}
return 0;
}
The iterative way of reversing a linked list is very easy way to do. I tried to understand the recursive way by going through the below link
http://www.geeksforgeeks.org/write-a-function-to-reverse-the-nodes-of-a-linked-list/
void recursiveReverse(struct node** head_ref)
{
struct node* first;
struct node* rest;
/* empty list */
if (*head_ref == NULL)
return;
/* suppose first = {1, 2, 3}, rest = {2, 3} */
first = *head_ref;
rest = first->next;
/* List has only one node */
if (rest == NULL)
return;
/* reverse the rest list and put the first element at the end */
recursiveReverse(&rest);
first->next->next = first;
/* tricky step -- see the diagram */
first->next = NULL;
/* fix the head pointer */
*head_ref = rest;
}
We first move the pointer to the tail of the linked list.
During stack unwinding we stitch the links reversely.
But for all the stack unwinding calls, *headref = rest . So as first is changing during the stack unwinding to the previous stack value, why doesnt the rest too didnt change. I created 4 nodes and viewed the values through the gdb. The rest values during stack unwinding remained constant but the first values are changing. Why is it rest not changing.
While thinking in terms of changing pointers and values is a good way
to think about iterative programs, it is a confusing way to look at
recursive programs, because each recursive call creates its own local
variables, all with the same names, but possibly different values.
With a recursive function it is more helpful to assume that it works correctly for input of size n, and then verify its correctness for input of size n+1. If the "base case" (size 0 or 1) is covered, this then proves that it works for all inputs
In your case, let's suppose that recursiveReverse works OK for lists of length 3, and let's feed it a list of length 4 a->b->c->d by
calling recursiveReverse(&p) where p=a
Both first->next and rest will be b, hence point to a 3-element list b -> c -> d, so (by our assumption)
recursiveReverse(&rest) will correctly reverse this list. After the
call, rest has changed value (from b to d) and now points to this reversed list d->c->b
first->next is still the same pointer b as before the call, and therefore now points to the end of the list.
Thus, first->next->next = first attaches first to the end of this reversed list, which then becomes d->c->b->a)
As first is now the end of the list, we now need first->next = NULL. The final step is to change *head_ref (from a to d), so, after returning from recursiveReverse(&p), p will have changed from a to the new head of the list, d.
This shows that whenever the function works correctly for n-element
lists, it works correctly for n+1-element lists. The base case is easy,
so whe have shown that it works for all lists.
Now, why don't you see rest changing value in your debugger? Because
its value is only ever changed by the function call
recursiveReverse(&rest). Before you recursively call
recursiveReverse it has one value, after you return from it it has
another, you don't see it changing when stepping in and out of each function call. The assignment that changes its value is actually the very last (*head_ref = rest) before the function returns, but the assignee is called head_ref in this stack frame, not rest (as it was called in the stack frame of the caller)
This is the "same names, but different values" confusion I mentioned above.
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 at an interview for a C position in which they presented me with an idiom that I haven't previously encountered. This is a trick that simplifies implementation of various algorithms involving linked lists and I'm wondering if anybody else has encountered this.
Say we have a linked list record defined so:
typedef struct _record
{
char* value;
struct _record* next;
} record;
We need a function that inserts a new record so that the entire list remains sorted with respect to the value's in the records. The following implementation is simpler than anything I would have used, albeit less readable.
void insert_sorted(record** r, const char* value)
{
record* newrec = NULL;
while(*r && strcmp(value, (*r)->value) > 0)
r = &((*r)->next); /* move r to point to the next field of the record */
newrec = malloc(sizeof(record));
newrec->value = strdup(value);
newrec->next = *r;
*r = newrec;
}
When the function is called, r points to the head pointer of the list. During the while loop, r is updated to point to the next field of the record that comes just before the point where we want to put the new record in. The last line of the function either updates the head pointer of the list (if the insertion happens at the beginning) or the next field of the previous record, which is quite cool.
A couple of questions:
Does this idiom have a name or is it mentioned in any literature?
Are there others like it in the C language?
I thought I knew C pretty well and had pointers and indirection pretty well figured out, but this one took me a while to fully understand.
I've used similar to this to insert into a binary tree. Because when iterating the tree, you usually stop when your pointer becomes NULL (you ran off the tree).
So to insert, you have 3 options,
1: use a variable which tracks the previous value of your iterating pointer.
2: stop when the pointer you would follow is NULL before you follow it, works but slightly less elegant in my opinion.
3: or a more elegant solution is simply use a pointer to a pointer, so you can just do: *it = new_node(); and it'll add it where the NULL used to be in your tree.
For a linked list, while this code works nicely, I usually just use a doubly linked list which makes it trivial to insert at any location.
I'd say the idiom is "the kind of code which gave 'c' a bad name"
Unwarrantedly clever
Unwarrantedly compact
Surprising side effects on caller
No error handling on malloc
Only works for US English strings
I don't see anything I'd call an idiom per se. It looks like standard coding for when you deal with datastructures in C.
My only complaint would be that the callers pointer (*r) is modified. Depending on the use of the function, I'd expect thats an unexpected side effect. Besides removing the unexpected side effect, using a local variable to play the role of *r would make the code more readable.
What would be the idiom here? Surely not the implementation of a linked list.
The usage of a pointer to pointer construct?
The compact loop?
Personally I'd use a pointer return value instead of operating on an input value.
Because seeing this input data type would ring a bell, which makes me copy my value before handing it to your function.
This is a well known thing - double pointer iteration (that's my name for it, I don't know the official name). The goal is to be able to locate a position in single linked list, and then insert before that position (inserting after it is trivial). Implemented naively, this requires two pointers (current and prev) and special code for beginning of the list (when prev == NULL).
The code the way I usually write it is something along the lines of
void insertIntoSorted(Element *&head, Element *newOne)
{
Element **pp = &head;
Element *curr;
while ((curr = *pp) != NULL && less(curr, newOne)) {
pp = &(pp->next);
}
newOne->next = *pp;
*pp = newOne;
}
Update:
I've forgot theother purpose for this trick - a more important one. It's used to delete elements from single linked lists:
// returns deleted element or NULL when key not found
Element *deleteFromList(Element *&head, const ElementKey &key)
{
Element **pp = &head;
Element *curr;
while ((curr = *pp) != NULL && !keyMatches(curr, key)) {
pp = &(pp->next);
}
if (curr == NULL) return NULL;
*pp = (*pp)->next; // here is the actual delete
return curr;
}
I don't know if this has a name or even if it's some special idiom but, since memory is relatively plentiful nowadays, my linked lists (where the language libraries don't make them available) are a special variant which simplifies the code greatly.
For a start, they're always doubly-linked since this makes traversal in both directions easier, for both processing and insert/delete operations.
An 'empty' list actually consists of two nodes, the head and the tail. By doing this, inserts and deletes do not need to worry about whether the node they're deleting is the head or tail, they can just assume it's a middle node.
Insertion of a new node y before node x then become a simple:
x -> prev -> next = y
y -> next = x
y -> prev = x -> prev
x -> prev = y
Deletion of node x is a simple:
x -> prev -> next = x -> next
x -> next -> prev = x -> prev
free x
Traversal is adjusted to ignore the extraneous head and tail:
n = head -> next
while n != tail
process n
n = n -> next
This all serves to make the code a lot easier to understand without all the special handling of the edge cases, at the cost of a couple of nodes of memory.
Instead of returning the value of the new node as an in/out parameter, you are better off having that be the return value of the function. This simplifies both the calling code, and the code inside the function (you can get rid of all those ugly double indirections).
record* insert_sorted(const record* head, const char* value)
You are missing error handling for the malloc/strdup failing case btw.
This idiom is given in the Chapter 12 of "Pointers on C".This is used to insert a node into a linked list without list head.
To answer the original question, this is known as a pointer centric approach instead of the naive node centric approach. Chapter 3 of "Advanced Programming Techniques" by Rex Barzee available at amazon.com includes a much better example implementation of the pointer centric approach.
I have also come up with this use of a double pointer, I have used it, but I don't really like it. The code that I came up with has this kernel to search for certain objects and remove them from the list:
Element** previous = &firstElement, *current;
while((current = *previous)) {
if(shouldRemove(current)) {
*previous = current->next; //delete
} else {
previous = ¤t->next; //point to next
}
}
The reason I don't like my code is the subtle difference between the two if clauses: the syntax is almost identical, but the effect is entirely different. I do not think, we should write code as subtle as this, but doing it differently makes the code really lengthy. So, either way is bad - you might go for brevity or for readability, it's your choice.
despite of the tricks, isn't the role of variable "r" changed? how does the caller tell what "*r" is for after calling? or after execution, what is the header of the list?
I could not believe this can be exemplified (even in some book?!).
Did I miss anything?
If you do not return any pointer (like the others suggested), then I would suggest following changes to keep the role of the input.
void insert_sorted(record** head, const char* value)
{
record** r = head;
bool isSameHead = false;
record* newrec = NULL;
while(*r && strcmp(value, (*r)->value) > 0) {
r = &((*r)->next); isSameHead = true; }
newrec = malloc(sizeof(record));
newrec->value = strdup(value);
newrec->next = *r;
*r = newrec;
if (!isSameHead) *head = newrec;
}
actually, probably another better way to do it is using the "dummy head node", which links its next to the beginning of the list.
void insert_sorted(record** head, const char* value)
{
record dummyHead;
dummyHead.next = *head;
record* r = &dummyHead;
while(r->next) {
if(strcmp(value, r->next->value) < 0)
break;
r = r->next;}
newrec = malloc(sizeof(record));
newrec->value = strdup(value);
newrec->next = r->next;
r->next = newrec;
*head = dummyHead.next;
}