I was reading about compound literals, and I saw that they are l-values. So I suspected that you can assign them, so I did an experiment and I noticed that this compiles without warnings with gcc -Wall -Wextra -pedantic
:
struct foo {
int x;
int y;
};
int main(void) {
(struct foo){0,0} = (struct foo){1,1};
}
It got me confused, because I really cannot see any situation at all where this could be useful. When would you ever want to assign anything to a compound literal?
Or is it one of the usual undefined behaviors? Because it looks very much alike modifying string literals. This compiles without warnings with the same parameters as above:
struct foo *ptr = &(struct foo){0,0};
*ptr = (struct foo){1, 1};
char *str = "Hello";
*str = 'X'; // Undefined behavior
Modifying the string is undefined behavior according to C standard 6.4.5.7
Well, it might not be useful, but it is the one that has least restrictions.
Since a non-const-qualified compound literal of non-array type is a modifiable lvalue, and modifiable lvalues can be assigned to among all things that you can do to modifiable lvalues, then you can assign to compound literals.
The opposite would be that you would have some extra cases for what you cannot do to lvalues that are compound literals.
I found out a use case for this that would work would assignment result in lvalue, which it doesn't:
foo(&((struct baz){0} = bar()))
here bar
returns a struct baz
as a value, and foo
needs a pointer to such struct
as an argument. Without this feature you cannot do such value passing inline.
What is the goal of compound literals?
The goal is to create a fully usable on the stack without providing a name for it.
Does "fully usable object on the stack" entail being an lvalue?
Yes. One of the typical uses for compound literals is to have their address taken and being passed on to some function. The goal is, that, whenever I have a function that takes a Foo* fooPtr
, I can supply the argument as a compound literal &(Foo){...}
. Since I'm taking the address and need to pass the non-const pointer on to the function, the compound literal must be an lvalue.
Note that the function can then assign to the object with a simple *fooPtr = (Foo){...};
. Code like this is very typical for constructors, or functions that reset an object. The function does not know whether fooPtr
points to a compound literal, a named variable, or a memory chunk on the heap. The assignment is legal for all these cases.
You see, the fact that you can assign to a compound literal is simply a side effect of compound literals being lvalues. And compound literals are only really useful as an inline, on-the-fly object creation construct if they are really lvalues.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With