【问题标题】:Cryptic struct definition in CC中的神秘结构定义
【发布时间】:2014-08-30 01:17:20
【问题描述】:

我遇到了以下maze definition 代码:

typedef struct mazeNode {
    int hasCheese;
    int tag;
    struct mazeNode *left;
    struct mazeNode *right;
} maze_t;

maze_t maze = {
    .tag = 1,
    .left = &(maze_t) {
        .left = &(maze_t) {
            .left = &(maze_t) {},
            .right = &(maze_t) {}
        },
        .right = &(maze_t) {
            .right = &(maze_t) {}
        }
    },
    .right = &(maze_t) {
        .tag = 8,
        .left = &(maze_t) {},
        .right = &(maze_t) {
            .tag = 10,
            .left = &(maze_t) {
                .tag = 11,
                .left = &(maze_t) {
                    .hasCheese = 1,
                    .tag = 12
                }
            },
            .right = &(maze_t) {}
        }
    }
};

从链接的blog post 我了解到他们正在尝试用图中的奶酪定义二叉树。

但是,我似乎无法完全理解 C 代码应该做什么。如果有人能给我解释一下就好了。

【问题讨论】:

  • 提供的代码附有blog post,作者在其中描述了代码。
  • 他们正在使用compound literals
  • .left = &(maze_t){};.right = &(maze_t){}; 会更自然地初始化为 0NULL。就目前而言,层次结构中有一个额外的元素,其.tag 值为0.hasCheese 值为0(以及空左右指针)。

标签: c struct designated-initializer compound-literals


【解决方案1】:

此代码使用designated initializerscompound literals 的组合,它们都是C99 features,我链接到其他答案,我为这两个功能提供标准引号。

指定初始化器允许您使用指定特定字段来使用.fieldname = 进行初始化,链接文档中的一个示例是:

 struct point { int x, y; };

下面的初始化

struct point p = { .y = yvalue, .x = xvalue };

等价于

struct point p = { xvalue, yvalue };

使用的另一个特性是复合文字,它被用来创建未命名的静态对象,然后代码获取该对象的地址并将其分配给各自的指针leftright。然后它在未命名的对象中递归地使用此功能来设置它们各自的leftright 指针。

 .left = & (maze_t) { .... }
           ^^^^^^^^^^^^^^^^
           unnamed static object

这些未命名的对象只有在函数体之外使用时才是静态的,否则它们将具有自动存储持续时间,并且在您退出函数后将不复存在,因此像代码所做的那样获取它们的地址可能是不明智的.

作为参考,我在answer here 中提供了复合文字的标准引用。

需要注意的是,当使用指定的初始化器时,任何未显式初始化的字段都是initialized to zero,这在这种情况下实际上很重要,例如hasCheese 将被设置为0,除非另有明确设置。

虽然这些是 C99 功能,但并非所有编译器都支持或完全支持 C99,但我对 Visual Studio 的测试表明我们需要替换空的复合文字,例如:

left = &(maze_t) {}

使用NULL 来编译它。我提交了bug report

对错误报告的响应如下,但基本上这是一个 gcc/clang 扩展在工作:

这是一个 GNU 扩展。 Clang 支持它作为扩展(参见 clang 选项 -Wgnu-empty-initializer)。

写这个的标准方法是 {0},它将所有初始化为零 字段。

【讨论】:

  • 这个标准功能是否适用于所有平台。或者使用这些功能会影响便携性吗?
  • @liv2hak 它们都是 C99 特性,所以 gccclang 都支持它们。 Visual Studio 最近开始支持 C99 功能,但我不确定这些功能的支持程度。
  • @liv2hak 好的,Visual Studio 不喜欢这样的代码,所以不能完全移植。
  • 我想我应该在这里说明一下,这不是 Visual Studio 中的错误,因为空的复合文字实际上是一个 GNU 扩展,您可以通过启用 -pedantic 看到warning: use of GNU empty initializer extension [-Wgnu-empty-initializer]。将所有字段初始化为零的标准方法是{0}
  • @sjdowling 谢谢,当我收到错误报告回复时,我以为我更新了这个问题。显然我没有,现在让我这样做。
【解决方案2】:

代码根据自 1999 年以来 C 语言中允许的语法(C99 和 C11)初始化结构。

简要说明,您可以通过仅写入大括号 {} 中包含的结构的“成员”来初始化结构变量。

例如,给定以下结构:

 struct fractional_number_s { int numerator; unsigned int denominator; };

我们可以如下定义和初始化结构变量:

 struct fractional_number_s r = { .numerator = 3, .denominator = 7, };  

如您所见,编写成员就足够了,没有变量名r
初始化器中允许使用此语法。

此外,在普通赋值中,我们可以借助复合文字获得类似的语法,如下例所示:

 r = (struct fractional_numbers_s) { .numerator = 3, .denominator = 7 };  

在互联网上搜索这些主题:C 结构初始化程序C 复合文字,以获取更多信息(技术说明:ANSI C89 没有'没有这个语法,所以搜索 ISO C99 和 ISO C11)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-15
    • 1970-01-01
    • 2018-06-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多