C语言中语句的评估

4
struct 
{
    int a[2], b;
} 
arr[] = {[0].a = {1}, [1].a = {2}, [0].b = 1, [1].b = 2};

如何在C语言中评估此行代码?结构体的一般声明与此语句不同。在C中访问元素可以像[0].a[0].b这样进行。

声明后面的部分是变量arr的初始化。这只是在语句之后立即设置数组应包含的值。在这种情况下,使用了一种相对较新的语法,称为指定初始化程序,它是在C99标准中引入的,并仅在变量初始化期间使用(包括复合文字)。 - doynax
你已经声明了一个结构体数组,因此[0].a表示索引为0的结构体并访问其属性a - Rahul
是的,可以像这样访问元素 arr[0].b = 1; - M.M
@M.M 我猜 OP 是在想念 arr :) - Sourav Ghosh
@SouravGhosh 嗯,当然你必须指定变量名... - M.M
始终为struct命名一个标签名称。这样,通过gdb等工具可以深入地显示该结构。 - user3629249
6个回答

9

首先定义一个新的结构类型:

struct {
   int a[2], b;
}

它声明了一个具有两个成员的结构体:一个名为 a 的包含两个 int 元素的数组和一个名为 bint 变量。
接下来可以分解为以下内容,首先是变量:
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 初始化为:

  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;

在IT技术中,[0](例如)表示数组的第一个元素,但是需要用表示该数组的表达式作为前缀,因此是arr[0]...


3
这是一个声明而不是语句,这就是为什么紧跟着 = 的内容是一个初始化器而不是表达式。在初始化器中可以做的事情与表达式中可以做的不同。
这种语法的样子是为了类似于表达式中引用元素的方式。以下是无效的伪代码以解释其含义:
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}初始化。


3

在C语言中,也可以像这样访问元素:[0].a[0].b吗?

简而言之:只有当你编写指定初始化器时才能这样做。


你有两个部分,一个是结构定义,另一个是初始化。
对于初始化部分,使用指定初始化器。它采用以下形式:
designator:
[ constant-expression ]
. identifier

因此,在你的情况下,

struct 
{
    int a[2], b;
} 
arr[] = {[0].a = {1}, [1].a = {2}, [0].b = 1, [1].b = 2};

指定初始化程序指示编译器创建一个由两个结构元素(大小由提供的初始化器中最大索引决定,注1)组成的数组arr,并提供这些单独元素成员的初始值。
因此,在您的情况下,最大索引是1,因此数组arr的大小为2(基于0的索引)。
换句话说,[0].a = {1}试图将arr[0]元素的成员a的值初始化为1。这等效于arr[0].a[0]。对于所有其余情况也是如此。
需要注意的是,这不是设置a[0]a[1]的值。在这里,由于“部分初始化”(花括号包含的初始化器未提供数组中所有成员的初始值),arr[0].a[0]设置为1,而arr[0].a[1]设置为0。(注2)

注意1:

引用《C11》,第§6.7.9/P22章节

如果初始化大小未知的数组,则其大小由具有显式初始化器的最大索引元素确定。数组类型在其初始化器列表结束时完成。

注意2:

引用《C11》,第§6.7.9/P21章节(我强调

如果花括号包围的列表中的初始化程序少于聚合体的元素或成员,或者用于初始化已知大小的数组的字符串字面量中的字符数少于数组中的元素数,则聚合体的其余部分应隐式初始化为具有静态存储期的对象相同。


2

第一部分是定义一个结构体变量。通常你会看到这样的代码:

// 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; },因此我们处理具有两个成员的未命名结构体,一个名为a的2个int数组和一个名为b的int。
我们要声明的变量是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 的第二个元素发生了什么:当一个变量只被部分初始化时,所有剩余的部分都会被设置为 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 } };

1
你已经声明了一个结构体数组,[0].a[0].bC99 语法。
= 前使用 [index].fieldname 标识符来指定要初始化的嵌套子对象,该列表相对于与最近的大括号对应的子对象进行解释。
请参考 link

0

发布的代码存在一些错误的初始化语法。

我在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;

上述行重复了两次。

请更正语法并重新发布。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接