[英]Evaluation of a statement in C language
struct
{
int a[2], b;
}
arr[] = {[0].a = {1}, [1].a = {2}, [0].b = 1, [1].b = 2};
如何用C语言评估这一行? 结构的一般声明与此声明不同。 也可以像[0].a
, [0].b
一样访问C中的元素?
第一行是新结构类型的定义:
struct {
int a[2], b;
}
它声明了一个包含两个成员的结构:一个名为a
的两个int
和一个int
b
的数组。
接下来可以分解为如下变量:
arr[]
它定义了一个变量arr
,它是一个结构数组。 未定义数组的大小,因为变量已初始化(因此其初始化由此初始化定义):
{ [0].a = ... }
这是一个新的C(C99,它不是那么新......)语法来初始化结构化数据类型的内容: 指定的初始化程序 。
当您初始化某些内容时,将定义您正在初始化的内容的上下文(具有两个成员的结构数组)。 然后notation [0]
只引用数组的第一个成员(所以数组至少有一个元素),因为这个元素是结构化的[0].a
表示它的成员a
,它本身就是一个数组。 然后{ 1 }
也初始化了这个数组。 这里的技巧是这个数组成员的长度已经由类型定义定义:length 2,然后{ 1 }
初始化该数组,第一个元素等于1,第二个元素为0
(初始化int的默认值)。 等等。
在末尾:
{[0].a = {1}, [1].a = {2}, [0].b = 1, [1].b = 2};
将arr
初始化为:
a
初始化为1,0,其成员b
初始化为1 a
初始化为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}
初始化。
也可以像
[0].a
,[0].b
一样访问C中的元素?
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}
试图将arr[0]
中元素成员a
的值初始化为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; }
struct { int a[2], b; }
,所以我们处理与两名成员一名不愿透露姓名结构,2个整数数组称为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^^
大括号有点疯狂,因为我们有一个嵌套在数组( arr[]
)内的结构中的数组( int a[2]
arr[]
)。 如果你想知道a
的第二个元素发生了什么:当一个变量只是部分初始化时,所有剩余的部分都设置为0
。 所以这段代码确实将内部数组初始化为{1, 0}
和{2, 0}
。
实际代码中的初始化程序看起来有点不同。 它使用C99中引入的一个功能,称为“指定初始化器”。 使用普通的初始化程序,您必须按顺序列出值; 在C99中,您可以添加一个“指示符”,指出值的位置。 指示符可以是括号( [
]
)或a中的数组索引.
后跟成员名称。 指示符也可以链接: [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 } };
你已经声明了struct的数组和[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;
以上几行重复两次。
请更正语法并重新发布
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.