Yu Hao has answered with the standard, now some vulgarization.
Whenever you see a compound literal like:
struct S *s;
s = &(struct S){1};
you can replace it with:
struct S *s;
struct S __HIDDEN_NAME__ = {1};
s = &__HIDDEN_NAME__;
So:
main.c
#include <assert.h>
struct S {int i;};
/* static: lives for the entire program. */
struct S *s1 = &(struct S){1};
struct S *s2;
struct S *s3;
struct S *s4;
int f(struct S *s) {
return s->i + 1;
}
int main() {
/* Undefined behaviour: not defined yet.
* GCC 10 -fsanitize=undefined -ggdb3 -O0 -std=c99 gives at runtime:
* runtime error: member access within null pointer of type 'struct S' */
#if 0
assert(f(s2) == 1);
#endif
/* Lives inside main, and any function called from main. */
s2 = &(struct S){1};
/* Fine because now instantiated. */
assert(f(s2) == 2);
/* Only lives in this block. */
{
s3 = &(struct S){1};
/* Fine. */
assert(f(s3) == 2);
}
{
/* On GCC 10 -O0, this replaces s3 above due to UB */
s4 = &(struct S){2};
}
/* Undefined Behavior: lifetime has ended in previous block.
* On GCC 10, ubsan does not notice it, and the assert fails
* due to the s4 overwrite.*/
#if 0
assert(s3->i == 1);
#endif
}
Full compilation command:
gcc -fsanitize=undefined -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c