Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 361
avans Avatar asked Dec 17 '22 11:12

avans


2 Answers

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.

like image 152
Lundin Avatar answered Dec 31 '22 01:12

Lundin


There is nothing strange here. When we come to the =, the initialization starts and the declaration is completely done. The sizeof operator requires a complete type, which your struct is. This would not work for instance:

struct x;
int n = sizeof x;

Also, in your case you not only have a complete type. You have also declared an object s. And because of that, both &s.offset and &s is completely valid.

There some cases where backreferencing does not work. Here is one:

int x[] = { sizeof x }; 

which yields this error:

error: invalid application of ‘sizeof’ to incomplete type ‘int[]’
    2 |     int x[] = {sizeof x};
like image 21
klutt Avatar answered Dec 31 '22 02:12

klutt