struct
{
int a[2], b;
}
arr[] = {[0].a = {1}, [1].a = {2}, [0].b = 1, [1].b = 2};
如何用 C 语言评估这一行?struct 的一般声明与此声明不同。也可以像这样访问 C 中的[0].a
元素[0].b
?
struct
{
int a[2], b;
}
arr[] = {[0].a = {1}, [1].a = {2}, [0].b = 1, [1].b = 2};
如何用 C 语言评估这一行?struct 的一般声明与此声明不同。也可以像这样访问 C 中的[0].a
元素[0].b
?
第一行是新结构类型的定义:
struct {
int a[2], b;
}
它声明了一个具有两个成员的结构:一个由两个int
命名的 sa
和一个s 组成的数组int
b
。
接下来可以分解如下,首先是变量:
arr[]
它定义了arr
一个结构数组的变量。未定义数组的大小,因为变量已通过以下方式初始化(因此其大小由此初始化定义):
{ [0].a = ... }
这是用于初始化结构化数据类型内容的新 C(C99,不是那么新...)语法:指定初始化程序。
当您初始化某些东西时,您正在初始化的内容的上下文被定义(具有两个成员的结构数组)。然后符号[0]
只引用数组的第一个成员(因此数组至少有一个元素),并且由于这个元素是结构化的,所以[0].a
表示它的成员a
,它本身就是一个数组。然后这个数组也被初始化{ 1 }
。这里的诀窍是这个数组成员的长度已经由类型定义定义:长度 2,然后{ 1 }
用第一个元素等于 1 和第二个元素初始化该数组0
(初始化 int 的默认值)。等等。
在最后:
{[0].a = {1}, [1].a = {2}, [0].b = 1, [1].b = 2};
初始化arr
为:
a
初始化为 1,0 并且其成员b
初始化为 1a
初始化为 2,0,其成员b
初始化为 2如果您使用assignements,那么您可以编写如下内容:
struct { ... } arr[2];
arr[0].a[0] = 1;
arr[0].a[1] = 0;
arr[0].b = 1;
arr[1].a[0] = 2;
arr[1].a[1] = 0;
arr[1].b = 2;
其中[0]
(例如)表示数组的第一个元素,但需要以表示该数组的表达式作为前缀,所以arr[0]
...
这是一个声明,而不是一个语句,这就是为什么后面=
是一个初始化程序,而不是一个表达式。您在初始化程序中可以做的事情与您在表达式中可以做的事情不同。
语法看起来类似于表达式中引用元素的方式。无效伪代码解释含义:
struct {int a[2], b;} arr[];
arr[0].a = {1};
arr[1].a = {2};
arr[0].b = 1;
arr[1].b = 2;
该数组arr
的长度为 2,因为提供了两个元素的值。arr[0]
从传统上写的内容初始化{{1}, 1}
。arr[1]
从 初始化{{2}, 2}
。
也可以像这样访问 C 中的
[0].a
元素[0].b
?
TL;DR 仅当您编写指定的初始化程序时。
这里有两件事,结构定义和初始化。
对于初始化部分,使用指定的初始化器。它采取一种形式
designator: [ constant-expression ] . identifier
所以,在你的情况下,
struct
{
int a[2], b;
}
arr[] = {[0].a = {1}, [1].a = {2}, [0].b = 1, [1].b = 2};
指定的初始化器指示编译器创建一个包含两个结构元素的数组arr
(大小由提供的初始化器中的最大索引确定,注 1)并提供这些单独元素的成员的初始值。
因此,在您的情况下,最大索引为1
,因此数组arr
大小为 2(基于 0 的索引)。
可以这么说,[0].a = {1}
是试图将a
元素成员的值初始化为arr[0]
to 1
。这与arr[0].a[0]
. 对于所有剩余的情况也是如此。
需要注意的是,这不是设置 和 的a[0]
值a[1]
。这里,由于“部分初始化”注2(大括号括起来的初始化程序不提供数组中所有成员的初始值),arr[0].a[0]
设置为1
并arr[0].a[1]
设置为0
。
注1:
引用C11
,第 §6.7.9/P22 章
如果初始化未知大小的数组,则其大小由具有显式初始化程序的最大索引元素确定。数组类型在其初始值设定项列表的末尾完成。
笔记2:
引用C11
,第 §6.7.9/P21 章(强调我的)
如果大括号括起来的列表中的初始值设定项少于聚合的元素或成员,或者用于初始化已知大小数组的字符串文字中的字符少于数组中的元素,则聚合的其余部分应隐式初始化与具有静态存储持续时间的对象相同。
第一部分是定义一个结构变量。通常你会看到这样的代码:
// define the type
struct foo { ... };
// define a variable of that type
struct foo x;
但是您可以将两者结合起来:
// define a type and a variable of that type
struct foo { ... } x;
在后一种情况下,您甚至不必命名类型:
// define a variable of an unnamed struct type
struct { ... } x;
在您的情况下,我们有struct { int a[2], b; }
,因此我们正在处理一个具有两个成员的未命名结构,一个名为 2 个 int 的数组a
和一个名为 的 int b
。
我们声明的变量是arr
. 名称后面的[]
意思是我们将它定义为一个数组。
通常我们会看到类似的东西:
// define an array of 2 elements
int arr[2];
我们可以添加一个初始化器:
// define and initialize an array of 2 elements
int arr[2] = { 100, 200 };
使用初始化器,我们不必明确说明数组有多大;它来自初始化程序:
// define and initialize an array of 2 elements
int arr[] = { 100, 200 };
将此应用于您的案例,您可能希望看到如下内容:
struct { int a[2], b; } arr[] = { { {1}, 1 }, { {2}, 2 } };
// ^a^ ^a^
// ^^struct^^ ^^struct^^
大括号有点疯狂,因为我们有一个数组 ( int a[2]
) 嵌套在一个嵌套在数组 () 内的结构中arr[]
。如果您想知道a
s 的第二个元素发生了什么:当变量仅部分初始化时,所有剩余部分都设置为0
. 所以这段代码真的将内部数组初始化为{1, 0}
and {2, 0}
。
实际代码中的初始化程序看起来有点不同。它使用了 C99 中引入的一个特性,称为“指定初始化器”。使用普通的初始化程序,您必须按顺序列出值;在 C99 中,您可以在前面加上一个“指示符”,说明值的去向。指示符可以是方括号 ( [
]
)中的数组索引,也可以是.
后跟成员名称的 a。指示符也可以链接:表示“将此数组元素的[0].b = 42
成员初始化为 42”。b
0
这就是这里发生的事情:
struct { int a[2], b; } arr[] = {[0].a = {1}, [1].a = {2}, [0].b = 1, [1].b = 2};
如果我们按索引对初始化器重新排序,我们会得到:
struct { int a[2], b; } arr[] = {[0].a = {1}, [0].b = 1, [1].a = {2}, [1].b = 2};
然后我们可以合并相邻的指示符:
struct { int a[2], b; } arr[] = {[0] = { .a = {1}, .b = 1 }, [1] = {.a = {2}, .b = 2} };
这使得我们更容易看到我们正在初始化两个元素(arr
大小为 2)以及实际值是什么。
所有这些形式都等价于:
struct { int a[2], b; } arr[] = { { {1}, 1 }, { {2}, 2 } };
您已声明 struct 数组和[0].a
,[0].b
是一种C99
语法。
an 之前的 and 指示符用于[index]
指定要初始化的嵌套子对象,该列表是相对于与最近的包围括号对对应的子对象进行的。.fieldname
=
参考这个链接。
发布的代码有一些不正确的初始化语法。
我gcc
在 ubuntu linux 16.04 上使用了编译器
编译器输出以下消息:
warning: missing initializer for field 'b' of 'struct <anonymous>' [-Wmissing-field-initializers]
arr[] = {[0].a = {1}, [1].a = {2}, [0].b = 1, [1].b = 2};
note: 'b' declared here
int a[2], b;
上述行重复两次。
请更正语法并重新发布