Self-referential variable initialization in C

(microblog.ahti.space)

20 points | by signa11 14 hours ago ago

4 comments

  • fanf2 3 hours ago ago

    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.
  • fuhsnn an hour ago ago

    For the compiler (or linker if global), it already has to prepare a space for the variable, the address is acquired from layout plan, not from referencing the variable at runtime, so the semantics is analogous to "fill number three in the third box."

  • Joker_vD 6 hours ago ago

    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-...

  • uecker 4 hours ago ago

    The traditional use case are circular linked lists or similar data structures.

    struct foo { struct foo *next; } x = { .next = &x };