The most common and idiomatic example that relies on a variable being in scope in its initializer is,
struct something *foo = malloc(sizeof(*foo));
But that isn’t, strictly speaking, self-referential. Here’s one that is…In BSD <sys/queue.h> there are a bunch of macros for various kinds of linked list.
https://man.freebsd.org/cgi/man.cgi?query=STAILQ_HEAD_INIT
https://cgit.freebsd.org/src/tree/sys/sys/queue.h
The STAILQ macros define a singly-linked list where the head is a pair of pointers (simplified slightly):
struct stailq_head {
struct stailq_elem *stqh_first;
struct stailq_elem **stqh_last;
}
• a pointer to the first element, which is NULL when the list is empty• a pointer to the NULL pointer that terminates the list
The last pointer allows fast appends, O(1) instead of O(n).
When you initialize the head, the last pointer needs to point to the first pointer. The STAILQ_HEAD_INITIALIZER() macro does basically:
struct stailq_head head = {
NULL,
&head.stqh_first,
};
There, head refers to itself!To append an element, the STAILQ_INSERT_TAIL() macro does basically:
elem->next = NULL;
*head.sthq_last = elem;
head.sthq_last = &elem->next;
So normally the last pointer points into an element; the last pointer points into the head only in an empty list.The traditional use case are circular linked lists or similar data structures.
struct foo { struct foo *next; } x = { .next = &x };
It's allowed because it's in the spirit of C: both allowing and prohibiting it is almost equally easy, and it's also sometimes useful, so it's allowed. Now, there is a way [0] to statically check whether self-referential value initialization is well-founded or not but... it's kinda tricky to do and so, again in the C's spirit, such diagnostics is not required.
[0] https://www.cl.cam.ac.uk/~jdy22/papers/a-right-to-left-type-...