【问题标题】:What is the need of empty braces '{ }' at the end of array of structs?在结构数组的末尾需要空括号“{}”是什么?
【发布时间】:2020-06-14 18:49:53
【问题描述】:

我在 Linux 内核中打了一些 code:

static struct ctl_table ip_ct_sysctl_table[] = {
    {
        .procname   = "ip_conntrack_max",
        .maxlen     = sizeof(int),
        .mode       = 0644,
        .proc_handler   = proc_dointvec,
    },
    // ...
    {
        .procname   = "ip_conntrack_log_invalid",
        .maxlen     = sizeof(unsigned int),
        .mode       = 0644,
        .proc_handler   = proc_dointvec_minmax,
        .extra1     = &log_invalid_proto_min,
        .extra2     = &log_invalid_proto_max,
    },
    { }
};

这里的结构数组以{ } 结尾。添加它的目的是什么?
顺便说一句,在这段代码的上方还有一个array of structs,但最后没有空括号。

什么时候应该在结构数组的末尾使用空括号?

【问题讨论】:

  • 嗯,如果它添加来表示数组的结束,就像 0 表示字符串的结束一样?只是猜测。
  • 这是一些非标准的 GCC 扩展。因此,它很可能带有很少或没有文档......我刚刚阅读了所有文档,但我找不到任何关于空结构初始化器列表的信息。但是它可以编译,除非您使用 -pedantic 强制执行严格的 ISO。
  • 无论如何,它是一个“哨兵”值,一个将所有内容都设置为零/NULL 以标记数组结尾的项。
  • 哨兵在CPython extension modules中也很常见。

标签: c c arrays struct linux-kernel


【解决方案1】:

此特定更改是 Eric W. Biederman 提交的 sysctl net: Remove unused binary sysctl code 的一部分,将 ip_ct_sysctl_table 数组的最后一个元素的初始化从 {0} 更改为 {}(并对许多其他数组执行类似的更改初始化)。

{0} 模式似乎已经存在了更长的时间,{0} 或 {} 最终元素初始化通常(在 Linux 源代码中)显式称为 Terminating entry,所以它可能存在一种模式,允许在不知道它们的长度的情况下使用这些数组,在达到零初始化终止条目时终止消费。例如。对于sound/aoa/fabrics/snd-aoa-fabric-layout.c 中的类似数组,甚至在评论中明确提到了零初始化的意图,例如:

static struct codec_connection toonie_connections[] = {
  {
      .connected = CC_SPEAKERS | CC_HEADPHONE,
      .codec_bit = 0,
  },
  {} /* terminate array by .connected == 0 */
};

【讨论】:

  • 了解他们放弃标准 C 以支持在功能方面 100% 等效的 GCC 扩展的理由会很有趣。它所做的只是阻止代码在标准 C 编译器上编译。也就是说,应该是 100% 等价的,因为 gcc 似乎没有记录这个特性......这 不是 一个零长度数组,它是一个空的初始化列表。
  • @Lundin 不会int arr[] = {}(鉴于我们使用的是 GNU 空初始化程序扩展)导致空数组;即arr的大小为0?
  • @Lundin:但是,cppreference 页面与 ISO/IEC 9899:2011 的措辞相冲突,后者允许这样做(§6.7.9 (21))。毫无疑问,没有初始化器比聚合的成员“少”。所以这不是一个奇怪的编译器扩展,而是合法的 C。
  • @Damon 它不是有效的 C 并且它是众所周知的......使用 gcc -pedantic-errors 编译。要了解原因,您需要阅读 6.7.9 顶部的初始值设定项列表的实际语法。必须至少有一个初始化程序。在这里解释:stackoverflow.com/questions/17589533/…。特别是 { initializer-list } 然后初始化列表:designation(opt) initializer 或 initializer-list , designation(opt) initializer
  • @Lundin 在这个特定的例子中,不知道。但是 gcc 扩展在 linux 内核中被广泛使用。
【解决方案2】:

在结构数组的末尾需要空括号'{}'吗?

要明确:“结构数组末尾的空括号'{}'”不是需要来满足C 语法要求。

什么时候应该在结构数组的末尾使用空括号?

当代码需要sentinel value时。

有时有用程序有一个全为零的最终数组元素 - 当然可以检测到结尾。 需要来自应用程序对数组ctl_table ip_ct_sysctl_table[]的使用,而不是来自C语言的需要。

【讨论】:

    【解决方案3】:

    它是数组末尾的一个零初始化元素,以便将数组元素的数量增加一。

    考虑这个小演示:

    #include <stdio.h>
    
    struct Test
    {
      int x;
      int y;
    } arr[] =
    {
        {1,2},
        {3,4},
    //  {}
    };
    
    int main(void) {
        printf("%zu\n", sizeof(arr) / sizeof(arr[0]));
        return 0;
    }
    

    如果您取消注释数组初始化列表末尾的{},arr 数组的大小将会改变。

    输出:

    使用// {}(数组有2个元素)

    2
    

    使用{}(数组有3个元素)

    3
    

    进一步说明:

    ip_ct_sysctl_table 数组只用在一个地方,就是这里:

    in->ctl_table = kmemdup(ip_ct_sysctl_table,
                    sizeof(ip_ct_sysctl_table),
                    GFP_KERNEL);
    

    额外的{} 增加了ip_ct_sysctl_table 的总大小。

    【讨论】:

    • 那不是“为了增加数组的元素个数”而是表示数组的结束。
    • 大声笑,不。这个想法是,到目前为止,没有人能够完全肯定地解释它。最接近的确定性声明就是 { } 是一个初始化器。但原因仍不清楚。因此,就目前而言,probably 这个词可能是个好主意。 :)
    【解决方案4】:

    您可能熟悉以零结尾的字符串。 ctl_table ip_ct_sysctl_table[] 是一个以零结尾的数组,即最后一个数组条目具有全零成员。

    【讨论】:

    • 所以通过数组,你知道你已经到达终点,例如procname 为 null,或 maxlen 为零。
    • @PaulOgilvie:这个例子不完整。 procname 可能是 char[100] 在这种情况下它是 "",而不是 null。但否则是的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-14
    • 2017-10-27
    • 1970-01-01
    • 1970-01-01
    • 2014-10-23
    • 2013-02-05
    相关资源
    最近更新 更多