【问题标题】:Evaluation of a statement in C language用 C 语言评估语句
【发布时间】:2017-04-15 06:48:14
【问题描述】:
struct 
{
    int a[2], b;
} 
arr[] = {[0].a = {1}, [1].a = {2}, [0].b = 1, [1].b = 2};

如何用 C 语言评估这一行? struct 的一般声明与此声明不同。也可以像 [0].a[0].b 这样访问 C 中的元素?

【问题讨论】:

  • 声明后的部分是变量arr的初始化。这只是在语句之​​后立即设置数组应包含的值。在这种情况下,使用了一种相对现代的语法,称为指定初始化程序,在 C99 标准中引入,仅在变量初始化期间使用(嗯,包括复合文字)。
  • 您已经声明了一个结构数组,因此[0].a 表示索引为0 的结构并访问它的属性a
  • 是的,访问元素可以像那个工具arr[0].b = 1;
  • @M.M 我猜 OP 想知道缺少 arr :)
  • @SouravGhosh 当然你必须指定变量名...

标签: c arrays initialization designated-initializer


【解决方案1】:

第一行是新结构类型的定义:

struct {
   int a[2], b;
}

它声明了一个具有两个成员的结构:一个由两个名为 aints 和一个 int b 组成的数组。

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

arr[]

它定义了一个变量arr,它是一个结构数组。数组的大小未定义,因为变量已通过以下方式初始化(​​因此其大小由该初始化定义):

{ [0].a = ... }

这是用于初始化结构化数据类型内容的新 C(C99,不是那么新...)语法:指定初始化程序

当您初始化某些东西时,您正在初始化的内容的上下文被定义(具有两个成员的结构数组)。然后符号[0] 仅引用数组的第一个成员(因此数组至少有一个元素),并且由于该元素的结构[0].a 表示其成员a,它本身就是一个数组。然后这个数组也被{ 1 }初始化。这里的技巧是这个数组成员的长度已经由类型定义定义:长度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]...

【讨论】:

    【解决方案2】:

    这是一个声明,而不是一个语句,这就是为什么= 后面的内容是一个初始化程序,而不是一个表达式。您在初始化程序中可以执行的操作与您可以在表达式中执行的操作不同。

    语法看起来类似于表达式中引用元素的方式。无效伪代码解释含义:

    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 这样完成?

      TL;DR 仅当您编写 designated initializer 时。


      这里有两件事,一个结构定义和初始化。

      对于初始化部分,使用指定的初始化器。它的形式是

      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] 设置为1arr[0].a[1] 设置为到0


      注1:

      引用 C11,第 §6.7.9/P22 章

      如果一个未知大小的数组被初始化,它的大小由最大的索引决定 具有显式初始值设定项的元素。数组类型在其末尾完成 初始化列表。

      注2:

      引用C11,第 §6.7.9/P21 章(强调我的

      如果大括号括起来的列表中的初始值设定项少于元素或成员的数量 聚合,或用于初始化已知数组的字符串文字中的更少字符 大小超过数组中的元素,聚合的其余部分应为 隐式初始化与具有静态存储持续时间的对象相同。

      【讨论】:

        【解决方案4】:

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

        // 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 个整数数组和一个名为 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 } };
        

        【讨论】:

          【解决方案5】:

          您已经声明了结构数组和 [0].a[0].bC99 语法。

          = 之前的 [index].fieldname 指示符用于指定要初始化的嵌套子对象,该列表是相对于与最近的括号对对应的子对象获取的。

          参考这个link

          【讨论】:

            【解决方案6】:

            发布的代码有一些不正确的初始化语法。

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

            以上行重复两次。

            请更正语法并重新发布

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2010-12-01
              • 2016-05-17
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2023-03-03
              相关资源
              最近更新 更多