Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is assignments to compound literals useful?

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

like image 850
klutt Avatar asked Oct 08 '20 10:10

klutt


2 Answers

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:

Since you just a moment ago stated that yo do not like macros, then I am not sure if this is going to convince you, but this makes it possible to write a macro that expands to something like
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.

like image 40
cmaster - reinstate monica Avatar answered Sep 21 '22 09:09

cmaster - reinstate monica