用 C 语言评估语句


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数组和一个intb

接下来可以分解为如下,首先是变量:

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初始化为:

  1. 长度为 2 的数组
  2. 它的第一个元素成员a初始化为 1,0,其成员b初始化为 1
  3. 它的第二个元素成员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]设置为1arr[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[])中的结构中。如果您想知道as 的第二个元素发生了什么:当变量仅部分初始化时,所有剩余部分都设置为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;

上述行重复两次。

请更正语法并重新发布

最新更新