Search code examples
cinitializationdeclarationdefinition

Initialization of variables from within the definition by referencing the type or a variable of that type


Is such heavy use of backreferencing a declaration from the initialization code covered by at least one of the standards (C99-current) or is it a gcc extension? All the member initialization terms have in common that they either reference the type or a member/variable of that type from within the definition of the type.

#include <stddef.h>
#include <stdlib.h>

struct node{
    int size;
    int offset;
    int *ptr;
    struct node *this;
} s = {sizeof(s), offsetof(struct node, offset), &s.offset, &s};

int main(void){
    struct node *s = malloc(sizeof(*s));
    free(s)
}

I googled around using the searchterms backreferencing declaration from definition, backreferencing declaration from initialization, c initialization struct referencing declaration etc, but all just provide me the differences between declaration and definition. However, i would like to know what is allowed by the standard when i reference the type or a member/variable of that type from within the definition.


Solution

  • In general, you can't refer to struct members from inside the declaration list. For example, this code has undefined behavior:

    typedef struct { int x; int y; } foo_t;
    
    foo_t foo = { .x=5, .y=x }; // BAD, the order of initialization between x and y is not defined
    

    For your specific case though, this does not apply. First of all, the struct definition ends at the } - from there on it is a complete type, something that has a full definition visible in the current file (pedantically: in the current translation unit).

    You then have these cases of initializers:

    • sizeof(s). Since you have a complete type (not a forward-declared struct or a variable-length array etc), using sizeof is fine. It yields an integer constant expression (C17 6.6/6) and is calculated at compile-time.
    • offsetof same deal as with sizeof.
    • &s.offset and &s. These are address constants, one type of constant expressions (C17 6.6/7). These are allowed inside an initializer list too and also calculated at compile-time.

    So all of your initializers are constant expressions - meaning the initializer list is valid. Even if you were to declare this struct with static storage duration.