head and tail point to the first and last entries respectively. There is just no such overhead entry as header but all data entries in list, so if there is only one entry in list, head and tail should point to the same it. Besides, there is such a snippet to delete all entries in list

The issue is that the comparison between values of memory address current and list->tail point to is evaluated after current pointer is freed. Supposing current and list->tail are now pointing to the same block of memory and then current is freed, what is the result of the evaluation and more importantly, is the result (whatever it is) deterministic in all different compilers in your experience? In my case, the program is compiled in MSVC and runs correctly, which says values of memory address current and list->tail point to are equal after current is freed.

4 Answers
4

The lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address, and retains its last-stored value throughout its lifetime. If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.

of the n1570 draft of the C2011 standard (almost identical in the same place in the C99 standard), after freeing current its value becomes indeterminate (and if current happened to point at the same object as list->tail before freeing it, the value of list->tail becomes indeterminate too). And thus using it in a comparison invokes undefined behaviour.

In a hypothetical implementation that tracks the validity of pointers and checks it for each comparison etc., it could crash or do other funny things.

However, in practice, I don't expect any implementation to do that, so it will almost certainly yield the expected result.

As WhozCraigpointed out, there actually is a platform doing that tracking. However, that treats all invalid pointers as NULL, so even there the code in question would produce the expected result, if I understood correctly.

@Deniel: that's why it's best practice to make pointer NULL after freeing it , right ?
–
OmkantNov 20 '12 at 13:09

1

Best practice is to have it (and all other pointers to the same object) go out of scope immediately after free. Setting it to NULL is second best.
–
Daniel FischerNov 20 '12 at 13:11

There is a platform that does enforce this, btw, Daniel. AS/400. All pointers are 128bit linear addresses, and there exists in a master address page a computational bit-set, where each bit represents a 16-byte offset of the process address space. Only when a pointer (which must be 16-byte aligned) holds a valid (meaning dereference'able) address is that bit lit. Any pointer value without its bit lit is treated as NULL. Amazingly, this means int *p = (int *)1; if (p) printf("foo"); prints nothing. Same setup, it means if (p == NULL) evals to true. It is an amazing system.
–
WhozCraigNov 20 '12 at 13:19

That is a great question. I never studied it beyond the omfg-they-really-do-that??-stage. A separate heap map that back-tracks their related pointer bit-offsets would be my guess. hard saying.
–
WhozCraigNov 20 '12 at 13:33

and more importantly, is the result (whatever it is) deterministic
in all different compilers in your experience

Memory, once allocated by the process, is usually not returned to the OS until the process terminates.
All compilers that I know of will evaluate current!=list->tail as false without throwing an exception.