Home > Back-end >  Structure initialization and assignment vs adhoc creation and assignment -- are they identical?
Structure initialization and assignment vs adhoc creation and assignment -- are they identical?

Time:10-02

Consider the following code:

struct Foo {
   int i;
   char c;
   float f;
};

int main() {
    struct Foo f1 = { .i = 1 };
    struct Foo f2;
    f2 = (struct Foo){ .i = 1 };
}

Afaik f1 is a structure partially initialized with designated initializer, and all its omitted fields are guaranteed to be initialized with zeros. But Does C standard guarantee that f1 would be identical to f2? And how exactly f2 creation syntax called?

CodePudding user response:

In this assignment statement

f2 = (struct Foo){ .i = 1 };

there is used a compound literal that creates an unnamed object of the type struct Foo that is initialized the same way as the object f1. Then this unnamed object of the type struct Foo is assigned to the object f2.

That is in this statement there is created one more object of the type struct Foo that has automatic storage duration.

Pay attention to that there is a typo in your code snippet

f2 = (struct Foo){ .i = 1 );
                         ^^^

you need to write

f2 = (struct Foo){ .i = 1 };
                         ^^^

CodePudding user response:

f2 = (struct Foo){ .i = 1 ); is called an assignment

They are not identical. The latter theoretically (or if you compile without optimizations) will create the object which time of life is the same as enclosing scope. This object will be used in the assignment

But in this trivial example, both will be optimized out. Optimizing compiler ( if the program is more complex) will optimize out the compound literal as you do not use reference to it, so the generated code will be identical in both cases

PS

Checked without the optimizations the unnamed object is not created.

main:
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-12], 0
        mov     DWORD PTR [rbp-4], 0
        mov     DWORD PTR [rbp-12], 1
        mov     QWORD PTR [rbp-24], 0
        mov     DWORD PTR [rbp-16], 0
        mov     DWORD PTR [rbp-24], 1
        mov     eax, 0
        pop     rbp
        ret

CodePudding user response:

But Does C standard guarantee that f1 would be identical to f2?

After the code shown in main, the named members of f1 and f2 will have identical values. Any padding bytes in the structures will not necessarily be identical. Thus, comparing f1 and f2 with memcmp(&f1, &f2, sizeof f1) may indicate they are different. Also, it is rare these days, but any padding bits in the members will not necessarily be identical.

And how exactly f2 creation syntax called?

(struct Foo){ .i = 1 }; is a compound literal, defined in C 2018 6.5.2.5. The grammar for a compound literal is ( type-name ) { initializer-list }, and there may be a comma at the end of the initializer list. A compound literal outside a function has static storage duration. A compound literal inside a function has automatic storage duration associated with its enclosing block.

CodePudding user response:

The assignment below:

struct Foo f2 = (struct Foo){ .i = 1 };

is using a compound literal. It is equivalent using an anonymous variable:

struct Foo _anonymous_ = { .i = 1 };
struct Foo f2 = _anonymous_;

The lifetime of the compound literal is the same as lifetime of a variable defined at the same scope.

Moreover it allowed to take an address of the literal or even do bizarre things like assigning values to it:

(int){ 0 } = 42; // valid !

In practice there is no functional difference for local variables, the compiler will likely treat compound literal and initializers the same it the context for the OP's question.

However, there is a difference for initialization of static and global objects because they must initialized with constant expressions. However, compound literal is not a constant expression.

static struct Foo f1 = { .i = 1 };  // ok
static struct Foo f2 = (struct Foo){ .i = 1 }; // illegal
  • Related