考虑以下代码:
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 是一个使用指定初始化器部分初始化的结构体,它的所有省略字段都保证用零初始化。但是 C 标准是否保证 f1 与 f2 相同?f2 创建语法究竟是如何调用的?
考虑以下代码:
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 是一个使用指定初始化器部分初始化的结构体,它的所有省略字段都保证用零初始化。但是 C 标准是否保证 f1 与 f2 相同?f2 创建语法究竟是如何调用的?
但是 C 标准是否保证 f1 与 f2 相同?
在 中显示的代码之后,和main
的命名成员将具有相同的值。结构中的任何填充字节不一定相同。因此,比较和与可能表明它们是不同的。此外,现在很少见,但成员中的任何填充位不一定相同。f1
f2
f1
f2
memcmp(&f1, &f2, sizeof f1)
f2 创建语法究竟是如何调用的?
(struct Foo){ .i = 1 };
是复合文字,在 C 2018 6.5.2.5 中定义。复合文字的语法是,并且在初始化列表的末尾可能有一个逗号。函数外的复合文字具有静态存储持续时间。函数内的复合文字具有与其封闭块关联的自动存储持续时间。( type-name ) { initializer-list }
f2 = (struct Foo){ .i = 1 );
被称为作业
它们不相同。后者理论上(或者如果您在没有优化的情况下编译)将创建生命周期与封闭范围相同的对象。该对象将在分配中使用
但在这个简单的例子中,两者都将被优化。优化编译器(如果程序更复杂)将优化复合文字,因为您不使用对它的引用,因此生成的代码在两种情况下都是相同的
附言
未经优化检查,未创建未命名对象。
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
在这个赋值语句中
f2 = (struct Foo){ .i = 1 };
使用了一个复合字面量,它创建了一个未命名的对象,该对象的struct Foo
初始化方式与 object 相同f1
。然后将这个类型struct Foo
的未命名对象分配给 object f2
。
也就是说,在此语句中,又创建了一个struct Foo
具有自动存储持续时间的类型的对象。
请注意,您的代码片段中有错字
f2 = (struct Foo){ .i = 1 );
^^^
你需要写
f2 = (struct Foo){ .i = 1 };
^^^
下面的作业:
struct Foo f2 = (struct Foo){ .i = 1 };
正在使用复合文字。它等效于使用匿名变量:
struct Foo _anonymous_ = { .i = 1 };
struct Foo f2 = _anonymous_;
复合文字的生命周期与在同一范围内定义的变量的生命周期相同。
此外,它允许获取文字的地址,甚至可以做一些奇怪的事情,比如给它赋值:
(int){ 0 } = 42; // valid !
实际上,局部变量没有功能差异,编译器可能会将复合文字和初始化程序与 OP 问题的上下文相同。
但是,静态对象和全局对象的初始化有所不同,因为它们必须使用常量表达式进行初始化。但是,复合文字不是常量表达式。
static struct Foo f1 = { .i = 1 }; // ok
static struct Foo f2 = (struct Foo){ .i = 1 }; // illegal