struct
{
int a[2], b;
}
arr[] = {[0].a = {1}, [1].a = {2}, [0].b = 1, [1].b = 2};
如何用C语言评估这一行?结构的一般声明与此声明不同。在 C 中访问元素也可以像[0].a
一样完成,[0].b
这样吗?
第一行是新结构类型的定义:
struct {
int a[2], b;
}
它声明了一个具有两个成员的结构:一个名为a
的两个int
数组和一个int
b
。
接下来可以分解为如下,首先是变量:
arr[]
它定义了一个变量arr
,它是一个结构数组。数组的大小未定义,因为变量是通过以下方式初始化的(因此其大小由此初始化):
{ [0].a = ... }
这是一个新的C(C99,不是那么新...)语法来初始化结构化数据类型的内容:指定的初始值设定项。
当你初始化某些东西时,你正在初始化的上下文被定义(具有两个成员的结构数组)。然后符号[0]
只引用数组的第一个成员(所以数组至少有一个元素),并且由于这个元素是结构化的[0].a
表示它的成员a
,它本身就是一个数组。然后这个数组也由{ 1 }
初始化。这里的诀窍是这个数组成员的长度已经由类型定义定义:length 2,然后{ 1 }
初始化该数组,第一个元素等于 1,第二个元素等于0
(初始化整数的默认值)。等。
最后:
{[0].a = {1}, [1].a = {2}, [0].b = 1, [1].b = 2};
将arr
初始化为:
- 长度为 2 的数组
- 它的第一个元素成员
a
初始化为 1,0,其成员b
初始化为 1 - 它的第二个元素成员
a
初始化为 2,0,其成员b
初始化为 2
如果你使用赋值,那么你可以写一些类似的东西:
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}
初始化的。
像
[0].a
一样访问 C 中的元素,[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
两个结构元素的数组(大小由提供的初始值设定项中的最大索引 Note 1 确定),并提供这些单个元素的成员的初始值。
因此,在您的情况下,最大的索引是1
,因此数组arr
大小为 2(从 0 开始的索引)。
可以这么说,[0].a = {1}
正在尝试初始化元素的成员a
的值,arr[0]
到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 个整数组成的数组称为a
和一个称为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}
和{2, 0}
.
实际代码中的初始值设定项看起来有点不同。它使用 C99 中引入的功能,称为"指定初始值设定项"。使用普通的初始值设定项,您必须按顺序列出值;在 C99 中,您可以在前面加上一个"指示符",说明值的去向。指示符可以是括号([
]
)中的数组索引,也可以是后跟成员名称的.
。指示符也可以链接:[0].b = 42
表示"将此数组的元素0
的成员b
初始化为 42"。
这就是这里发生的事情:
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 } };
你有声明结构数组和[0].a
,[0].b
是一种C99
语法。
[index]
和.fieldname
指示符之前=
指定要初始化的嵌套子对象,该列表是相对于对应于周围最接近的大括号对的子对象。
请参阅此链接。
发布的代码有一些不正确的初始化语法。
我在 ubuntu linux 16.04 上使用gcc
编译器编译器输出以下消息:
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;
上述行重复两次。
请更正语法并重新发布