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;
}
Related
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 ) ) )
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;
}
This might seem like a silly question to some of you and I know that I get things mixed up quite often but I need to understand the code so I can stop obsessing about it and focus on the real matter of why I need to use it.
So, in the code I see several assignments like this:
struct bst_node** node = root;
node = &(*node)->left;
node = &(*node)->right;
is there an invisible parenthesis here?
node = &((*node)->right);
This example is taken from literateprograms.org.
So to me it seems &(*node) is unnecessary and I might as well just write node->left instead, but the code seems to work where I can't make sense of it and I'm wondering if it's because I'm misunderstanding what's happening at those lines. Particularly, at one place in the code where it is deleting a node by constantly moving the "deleted" data to the bottom of the tree to safely remove the node without having to "break things", I'm lost because I don't get how
old_node = *node;
if ((*node)->left == NULL) {
*node = (*node)->right;
free_node(old_node);
else if ((*node)->right == NULL) {
*node = (*node)->left;
free_node(old_node);
} else {
struct bst_node **pred = &(*node)->left;
while ((*pred)->right != NULL) {
pred = &(*pred)->right;
}
psudo-code: swap values of *pred and *node when the
bottom-right of the left tree of old_node has been found.
recursive call with pred;
}
can keep the tree structure intact. I don't understand how this makes sure the structure is intact and would appreciate some help from somebody who knows what's going on. I interpret node being a local variable on the stack, created at the function call. Since it is a double pointer it points to a location in the stack (I assume this, since they did &(*node) previously to the function call), of either it's own stack or the function before, which then points to said node on the heap.
In the example code above what I think it is supposed to do is switch either left or right, since one of them is NULL, and then switch the one that isn't (assuming the other one isn't NULL?) As I said, I'm not sure about how this would work. My question mostly relates to the fact that I think &(*node) <=> node but I want to know if that's not the case etc.
node = &(*node)->right;
is there an invisible parenthesis here?
node = &((*node)->right);
Yes. It is taking the address of the right member of *node. The -> takes precedence over &; see C++ Operator Precedence (-> is 2 and & is 3 in that list) (it's the same general precedence as C).
So to me it seems &(*node) is unnecessary and I might as well just write node->left instead,
Your premise is off. There is no expression &(*node), as explained above, the & applies to the entire (*node)->left, not (*node).
In that code the double pointers are just that, a pointer to a pointer. Just as this works:
int x = 0;
int *xptr = &x;
*xptr = 5;
assert(x == 5);
This is the same, it changes the value of the pointer x:
int someint;
int *x = &someint;
int **xptr = &x;
*xptr = NULL;
assert(x == NULL);
In that code snippet you posted, assigning a pointer to *node changes the value of the pointer that node points to. So, e.g. (pseudo-ish code):
typedef struct bst_node_ {
struct bst_node_ *left;
struct bst_node_ *right;
} bst_node;
bst_node * construct_node () {
return a pointer to a new bst_node;
}
void create_node (bst_node ** destination_ptr) {
*destination_ptr = construct_node();
}
void somewhere () {
bst_node *n = construct_node();
create_node(&n->left); // after this, n->left points to a new node
create_node(&n->right); // after this, n->right points to a new node
}
Noting again that &n->left is the same as &(n->left) because of precedence rules. I hope that helps.
In C++ you can pass arguments to a function by reference, which is essentially the same as passing a pointer except syntactically it leads to code that is a bit easier to read.
That is useful
&(*node)->left <=>&((*node)->left)
The variable edited by this code is *node. I need the context fo this code to give more info
I'm having trouble understanding a piece of C code that represents a linked list structure. The skeleton of the struct looks like this:
struct r{
r *next;
r **prev;
data *d;
}
struct r *rlist;
rlist can be filled by calling the following function: (skeleton only)
r* rcreate(data *d){
struct r *a = xmalloc(sizeof(*r))
a->d = d;
a->next = rlist;
a->prev = &rlist;
if (rlist)
rlist->prev = &a->next;
rlist = a;
return a;
}
How do I go about using this data structure? e.g. how to traverse rlist ?
Edit: here is the function for deleting a node in the linked list
void rdestroy(struct r *a){
if (a->next){
a->next->prev = a->prev;
}
*a->prev = a->next;
destroy(a->d); /* destroy is defined elsewhere */
}
Double prev pointer seems to allow traversing list in one direction only, while allowing easy deletion (because even though you can't access the previous element (easily), you can access the next pointer of previous element, and set it to new correct value when deleting a node.
Without seeing other related functions, it's hard to see why it is done this way. I've not seen this done, and can't immediately think of any really useful benefit.
I think this allows having simpler node deletion code, because node does not need to care if it first or not, because node's prev pointer will always have non-NULL value to a pointer it needs to modify when deleting itself. And same simplicity for insertion before a current node. If these operations are what dominate the use pattern, then this could be seen as minor optimization, I suppose, especially in older CPUs where branches might have been much more expensive.
How to traverse list
This was the question, right? You can only traverse it forward, in a very simple manner, here's a for loop to traverse entire list:
struct r *node;
for (node = rlist ; node ; node = node->next) {
// assert that prev points to pointer, which should point to this node
assert(*(node->prev) == node);
// use node
printf("node at %p with data at %p\n", node, node->d);
}
Example insertion function
This example insertion function demonstrates how insertion before a node needs no branches (untested):
struct r *rinsert(struct r *nextnode, data *d) {
// create and initialize new node
struct r *newnode = xmalloc(sizeof(struct r));
newnode->d = d;
newnode->next = nextnode;
newnode->prev = nextnode->prev;
// set next pointer of preceding node (or rlist) to point to newnode
*(newnode->prev) = newnode;
// set prev pointer of nextnode to point to next pointer of newnode
nextnode->prev = &(newnode->next);
return newnode;
}
There's no good reason to have r ** next in that structure. It's for a double linked list.
So if this thing is created you have it assigned
thisList = rcreate("my data")
now you could start with traversing it
while (thisList->next)
thisList = thisList->next.
...
Your code has many syntactical errors in it, probably because (as you say) it is a "skeleton," so it is hard to parse what the author (whether it was you or someone else) actually intended this code to do.
A simple (doubly) linked list structure looks like this:
struct node {
struct node *next, *prev; // pointers to the adjacent list entries
int data; // use whatever datatype you want
};
struct node *list = NULL; // the list starts empty
void add_entry(int new_data) {
struct node *new_entry = malloc(sizeof(struct node));
// note that in the above line you need sizeof the whole struct, not a pointer
new_entry->data = new_data;
new_entry->next = list; // will be added to the beginning of the list
new_entry->prev = NULL; // no entries currently front of this one
// in general a NULL pointer denotes an end (front or back) of the list
list->prev = new_entry;
list = new_entry; // now list points to this entry
// also, this entry's "next" pointer points to what used to
// be the start of the list
}
Edit: I'll say that if you want us to help you understand some code that is part of a larger program, that you did not write and can't modify, then please post the relevant code in a format that is at least syntactical. As others have said, for example, the use of prev in the code you posted is indecipherable, and it isn't clear (because there are other similarly confusing syntactical problems) whether that was in the original code or whether it is an error introduced in transcription.
Yang, I am not sure how comfortable you are with pointers in general. I suggest taking a look at few other linked-list implementations, it might just do the trick.
Take at look at this Generic Linked List Implementation.
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)!