6

通过struct复合文字进行初始化,它将自己进行转换。例如:

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
};

但是,如果我分开定义,我必须添加一个显式转换:

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

代码:https ://godbolt.org/z/dG8nMh

如果我(cast_type)在第二个中省略了,我会得到一个错误 error: expected expression before ‘{’ token。为什么会这样?

也就是说,为什么初始化不需要强制转换但其他定义需要?我的想法是第二个版本应该能够在没有显式转换的情况下自行解决,但显然这是不正确的。

4

5 回答 5

1

其他答案涵盖了复合文字的初始化和赋值之间的区别。然而,还有另一个概念点需要提出:

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

任何一种情况下,您都在创建一个链表并将头部设置为指向一个节点,该节点既不是动态分配的,也不是某些应用程序定义的池的一部分。链表的成员通常是通过池动态创建malloc或从池中获取的。

您现在拥有的是列表中的一个“特殊”节点,因为它没有像添加其他节点那样添加到列表中,这意味着您需要额外的逻辑来跟踪它并以不同的方式处理它如果它被删除,则从列表中的其他节点。

最好将列表的头部初始化为NULL. 然后您的添加/删除功能需要知道列表是否为空。

于 2021-03-10T20:30:58.613 回答
1

在这两种情况下,您都使用复合文字。

在声明中的第一种情况下

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

您在初始化器列表中使用(Node){{"Avatar", 2010}, NULL}类型的复合文字,Node其地址用作head结构数据成员的初始化器LinkedList

在第二种情况下,您首先创建了一个类型的对象LimkedList

LinkedList movies2;

然后您使用类型的复合文字对创建的对象使用赋值运算符LinkedList

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

那是

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

那就是没有任何铸造。使用了两种不同的复合文字。Node 类型和 LinkedList 类型之一。

说清楚。考虑一个简单的例子

int x = { 10 };

在上面的声明中,变量x由整数常量 10 初始化。

您也可以通过以下方式编写

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

这里创建了一个中间变量来初始化变量x。复合文字实际上是一个未命名的对象。上面的代码可以重写为

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

这里没有任何铸造。这个表达式 ( int ){ 10 }创建了一个int由整数常量 10 初始化的类型的未命名对象。并且这个新创建的未命名对象被分配给变量x

另请参阅以下问题在 C 语言中使用“{}”进行强制转换有什么好处?

于 2021-03-10T20:02:56.410 回答
1

似乎是显式转换是复合文字 semathics 的一部分(type_name){ }

复合文字的语法可能与类型转换混淆,但是转换是非左值表达式,而复合文字是左值。

C11 N1570 §6.5.2.5

语义

  1. 由带括号的类型名称后跟用大括号括起来的初始化器列表组成的后缀表达式是复合文字。它提供了一个未命名的对象,其值由初始化列表给出。

第一个是初始化器,而不是赋值,所以不需要它。


我还要注意,在表达式.head = &(Node){{"Avatar", 2010}, NULL}中,复合文字是左值,可以通过地址获取,但它的生命周期仅限于它所属的范围,在该范围之外访问它会调用未定义的行为,

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
于 2021-03-10T20:12:23.750 回答
0

在 C 中,左值和右值应该是同一类型。在表达式中

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

您确保 r 值是 LinkedList 类型。如果您删除 (LinkedList),则 r 值的类型是未知的。这就是错误的原因。

于 2021-03-10T20:02:32.683 回答
0

因为在第一个片段中,您正在使用这些值初始化您的结构。但是在您的第二个片段中,您正在创建一个复合文字,然后将其复制到您的结构中。

于 2021-03-10T19:55:02.997 回答