6

With struct initialization via a compound literal, it will do the casting itself. For example:

struct movie {
    char title[50];
    int year;
};
typedef struct movie Item;

typedef struct node {
    Item        item;
    struct node *next;
} Node;

typedef struct linkedlist {
    Node   *head;
    size_t size;
} LinkedList;
LinkedList movies2 = {
    .head=&(Node){{"Avatar", 2010}, NULL},
    .size=1
};

However, if I separate the definition, I have to add in an explicit cast:

LinkedList movies2;
movies2 = (LinkedList) {
    .head=&(Node){{"Avatar", 2010}, NULL},
    .size=1
};

Code: https://godbolt.org/z/dG8nMh

And if I leave out the (cast_type) in the second one I will get an error along the lines of error: expected expression before ‘{’ token. Why is this so?

That is, why does the initialization not need the cast but the other definition does? My thought was the second version should be able to resolve itself without the explicit cast but obviously that is incorrect.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
David542
  • 104,438
  • 178
  • 489
  • 842
  • 1
    It would help to show the definition for these things. – tadman Mar 10 '21 at 19:51
  • 1
    I think this is undefined behaviour. You're using the address of a temporary that gets trashed as soon as it falls out of scope. A cast is "necessary" because by default it doesn't want to work. – tadman Mar 10 '21 at 19:52
  • That you could be assigning an array to struct variable type? – James John Mar 10 '21 at 19:53
  • @tadman: these constructs are called [compound literals](http://port70.net/~nsz/c/c11/n1570.html#6.5.2.5) – pmg Mar 10 '21 at 19:53
  • @pmg Yeah, I know, but I think in this case it's not going to work. What's the lifetime of that inner literal? – tadman Mar 10 '21 at 19:55
  • 1
    @tadman updated with the structs. – David542 Mar 10 '21 at 19:56
  • The assignment creates a copy (even if the lifetime was to next sequence point it would be ok). – pmg Mar 10 '21 at 19:56
  • Normally you'd create this in one shot with the properties spelled out immediately, not as a two-stage thing. This involves an unnecessary copy. – tadman Mar 10 '21 at 19:59
  • @pmg I mean specifically the embedded `.head` assignment in that. – tadman Mar 10 '21 at 20:00
  • 1
    `(LinkedList) {}` is not a cast. It is a syntax for compound literals, which you seem to be aware of. – Eugene Sh. Mar 10 '21 at 20:01
  • [C11 6.5.2.5p5](http://port70.net/~nsz/c/c11/n1570.html#6.5.2.5p5) says it's static storage duration it outside a function or automatic storage duration. – pmg Mar 10 '21 at 20:02
  • @pmg, being an lvalue the address of a compound literal can be taken, but its lifetime is limited to the scope where it belongs. It has static storage duration if at file level. – anastaciu Mar 10 '21 at 20:54

5 Answers5

1

In the both cases you are using compound literals.

In the first case in the declaration

LinkedList movies2 = {
    .head=&(Node){{"Avatar", 2010}, NULL},
    .size=1
};

you are using the compound literal (Node){{"Avatar", 2010}, NULL} of the type Node in the initializer list the address of which is used as an initializer for the data member head of the structure LinkedList.

In the second case you at first created an object of the type LimkedList

LinkedList movies2;

and then you are using the assignment operator to the created object with the compound literal of the type LinkedList

(LinkedList) {
    .head=&(Node){{"Avatar", 2010}, NULL},
    .size=1
}

that is

movies2 = (LinkedList) {
    .head=&(Node){{"Avatar", 2010}, NULL},
    .size=1
};

That is there are no any casting. There are used two different compound literals. One of the type Node and other of the type LinkedList.

To make it clear. Consider a simple example

int x = { 10 };

in the declaration above the variable x is initialized by the integer constant 10.

You can write also the following way

int tmp = { 10 };
int x;
x = tmp;

Here there is created an intermediate variable to initialize the variable x. A compound literal in fact is an unnamed object. The code above may be rewritten like

int x;
x = ( int ){ 10 }; 

Here there is no any casting. This expression ( int ){ 10 } creates an unnamed object of the type int that is initialized by the integer constant 10. And this newly created unnamed object is assigned to the variable x.

See also the following question What are the advantages of using “{}” for casting in C Language?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

What appears to be an explicit cast is part of the compound literal semathics (type_name){ }.

The syntax of a compound literal can be confused with a typecast, however a cast is a non-lvalue expression whereas a compound literal is an lvalue.

C11 N1570 §6.5.2.5

Semantics

  1. A postfix expression that consists of a parenthesized type name followed by a brace-enclosed list of initializers is a compound literal. It provides an unnamed object whose value is given by the initializer list.

The first one is an initializer, not an assignment, so doesn't need it.


I would also note that in the expression .head = &(Node){{"Avatar", 2010}, NULL} the compound literal being an lvalue, can be taken by address, but its lifetime is limited to the scope where it belongs, accessing it outside that scope invokes undefined behavior, i.e.:

LinkedList getLinkedList()
{
    LinkedList movies2;
    movies2 = (LinkedList){
        .head = &(Node){{"Avatar", 2010}, NULL},
        .size = 1};       
    return movies2;
}
LinkedList l = getLinkedList();
printf("%s", l.head->item.title); // undefined behavior
anastaciu
  • 23,467
  • 7
  • 28
  • 53
1

The other answers have covered the difference between initialization and assignment from a compound literal. There's another conceptual point that needs to be brought up however:

LinkedList movies2 = {
    .head=&(Node){{"Avatar", 2010}, NULL},
    .size=1
};

In either case, you're creating a linked list and setting the head to point to a node that is not dynamically allocated nor part of some application defined pool. Members of a linked list are typically either created dynamically via malloc or taken from a pool.

What you have now is one node of the list that is "special" in that it hasn't been added to the list the way other nodes would have been added, meaning you would need extra logic to keep track of it and handle it differently from other nodes in the list if it were removed.

It would be better to initialize the head of the list to NULL. Then your add / delete functions would need to know whether or not the list is empty.

dbush
  • 205,898
  • 23
  • 218
  • 273
0

Be cause in the first snippet you are initializing your struct with those values. But in your second snippet you are creating a compound literal and then copying it to your struct.

0

In C, the l-value and r-value are expected to be of same type. In the expression

movies2 = (LinkedList) {
.head=&(Node){{"Avatar", 2010}, NULL},
.size=1 };

you are making sure the r-value is of type LinkedList. If you drop (LinkedList), the r-value's type is not known. That's the reason for the error.