【问题标题】:Initializing constant struct containing pointer to its own type初始化包含指向其自身类型的指针的常量结构
【发布时间】:2013-07-19 09:05:40
【问题描述】:

我有一个这样的结构:

typedef struct tree_s{
    struct tree_s *a;
    int b;
}tree_t;

目前,我正在以这种方式进行初始化:

tree_t branch_n = {
    .a = NULL,
    .b = 2
};

tree_t root = {
    .a = (tree_t*) &branch_n, 
    .b = 1
};

现在,我不得不在根之前初始化较低的分支,这让我很恼火,因为完整的结构非常大,分支都有自己的分支,这使得我的代码难以管理。

我想做的是这样的:

tree_t root = {
    .a = 
     //The first branch
     {                 
        .a =
         //Yet another branch 
         { //Since the following is actually an array, I need the 
           // "a" above to point to the first index
             {  
                 .a = NULL,         //Maybe this will have its own branch
                 .b = 3
             },
             {
                 .a = 
                 {
                     .a = NULL,     //And this might even have its own branch
                     .b = 5
                 }
                 .b = 4  
             }
         }
        .b = 2
     }, 
    .b = 1
};

我怎样才能实现这样的初始化?

我想这样做的主要原因是为了大大增强我的代码概览,并立即直观地看到“树”的结构。

请注意,从一开始就知道完整“树”的结构,这就是我认为结构不变的原因。但是 b 的值可以随时更改。

我对 C 语言很陌生,这是我在 SO 上的第一篇文章,所以请随时编辑或询问我是否无法让自己清楚:)

【问题讨论】:

  • 那么问题是什么?
  • 我认为为此你应该将结构声明为typedef struct tree_s{ struct tree_s a; int b; }tree_t;
  • lulyon:对不起,我已经编辑包含一个实际问题:)。我无法以我想要的方式进行初始化(示例)。 Grijesh:我的编译器(IAR Embedded Workbench)收到错误“不允许不完整类型”:/
  • 您不能像 Grijesh 的评论那样声明结构;您只能声明一个包含指向其自身类型的指针的结构。

标签: c data-structures struct constants typedef


【解决方案1】:

选项 1a — 一个数组

您的“树”结构似乎卡在了相当线性的形状上,但您可以使用数组来解决这个问题:

static const tree_t oak[] =
{
    { .a = &oak[1], .b = 20 },
    { .a = &oak[2], .b = 15 },
    { .a = NULL,    .b = 10 },
};

选项 1b — 从数组构建的树

或者给定一个二叉搜索树结构:

#include <stdio.h>

typedef struct bst_t bst_t;
struct bst_t
{
    int          data;
    const bst_t *left;
    const bst_t *right;
};

static const bst_t bst[] =
{
    { .data = 30, .left = &bst[1], .right = &bst[2] },
    { .data = 10, .left = &bst[3], .right = &bst[4] },
    { .data = 50, .left = &bst[5], .right = &bst[6] },
    { .data =  5, .left = &bst[7], .right = &bst[8] },
    { .data = 20, .left =       0, .right = &bst[9] },
    { .data = 40, .left =       0, .right =       0 },
    { .data = 60, .left =       0, .right =       0 },
    { .data =  2, .left =       0, .right =       0 },
    { .data =  8, .left =       0, .right =       0 },
    { .data = 28, .left =       0, .right =       0 },
};

static void print_in_order(const bst_t *bst)
{
    if (bst != 0)
    {
        printf("[");
        print_in_order(bst->left);
        printf("(%d)", bst->data);
        print_in_order(bst->right);
        printf("]");
    }
}

static void print_tree(const bst_t *bst)
{
    print_in_order(bst);
    putchar('\n');
}

int main(void)
{
    print_tree(&bst[0]);
    return 0;
}

这会产生输出:

[[[[(2)](5)[(8)]](10)[(20)[(28)]]](30)[[(40)](50)[(60)]]]

选项 2a — C99 复合文字

使用 C99 和复合文字,您可以编写:

#include <stddef.h>

typedef struct tree_s{
    struct tree_s *a;
    int b;
}tree_t;

tree_t root2 =
{
    .a = &(tree_t){ .a = NULL, .b = 2 }, .b = 1
};

tree_t root3 =
{
    .a = &(tree_t){ .a = &(tree_t){ .a = NULL, .b = 3 }, .b = 2 }, .b = 1
};

我不确定它是否清晰,但它可以编译。不过,总的来说,我更喜欢数组表示法。

这是 OP 想要/使用的主要答案。


选项 2b - 尝试将数组初始化失败

尝试调整修改后的数据结构(请记住,可以使用该数据结构创建的“树”只是一个单链表),您几乎可以(但不完全)这样做:

tree_t root4 =
{
    .a = &(tree_t)
    {                 
        .a = (tree_t [])
        { 
            (tree_t){  
                .a = NULL,
                .b = 3
            },
            (tree_t){
                .a = &(tree_t)
                {
                    .a = NULL,
                    .b = 5
                },
                .b = 4  
            },
        },  // Line 47
        .b = 2
    }, 
    .b = 1
};

Mac OS X 10.8.4 上的 GCC (i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (基于 Apple Inc. build 5658) (LLVM build 2336.11.00)) 抱怨:

tree.c:47: error: initializer element is not constant
tree.c:47: error: (near initialization for ‘(anonymous)’)

第 47 行被标记的地方——它是结构数组的结尾。我可能遗漏了一些明显的东西。我确实尝试过&amp;(test_t[]){ ... }[0],,但得到了同样的警告。

我完全不知道你怎么知道特定指针是指向数组开头的指针而不是单个 tree_t 元素,除非你添加另一个字段来指示差异(或 @987654329 @ 字段以某种方式被编码以指示 a 是指向单个项目的指针还是指向数组的指针。

【讨论】:

  • 你好乔纳森,很好的例子。如果我希望“a”拥有自己的“a”,我将如何初始化(我现在看到我最好将其称为“孩子”:P)。我的意思是,我将如何初始化“橡树”的孙子,而不必在橡树之前声明它们。
  • BST 示例有帮助吗?你需要不同的节点有自己的名字吗?
  • 我不确定我是否无法说清楚,或者我只是不理解这个例子,非常感谢你的时间:)。我已经更新了我的问题中的示例,以尝试使其更清晰。是的,您的最后一个示例可能会起作用,因为它看起来像我目前正在做的那样,但这意味着我必须由他们自己声明成员,因此无法概述最终树的结构,因为它包含相当多的孩子/孙子/曾孙。
  • 您修改后的示例类似于我的root3 示例;我只是对结构进行了不同的格式化。但是,您的初始化在内部也存在问题;你丢失了一些 .a / .b 符号(指定的初始化器)。
  • 啊,是的,我现在明白了,这正是我想要做的。非常感谢你,你不会相信我花了多少时间来解决这个问题:)!实际上,我可以建议答案只是 struct 的 typedef 和 root3 的 init(第 3 到 6 行 + 24 到 27 行),这就是我解决问题所需要知道的。如果你选择不简化,我会接受这个作为答案 - 再次非常感谢你,乔纳森 :)
【解决方案2】:

我不认为这段代码:

tree_t root = {
    .a = 
     //The first branch
     {                 
        .a = NULL,       //Maybe another branch instead of NULL
        .b = 2
     }, 
    .b = 1
};

将成功初始化 tree_t 根。

{} 用于初始化结构,而不是指针。但是 root->a 被定义为一个指针。你可能会因此得到编译错误。

可以在上述代码之外定义和初始化分支,并设置指针root->指向它。

【讨论】:

  • 是的,你是对的,但这是我的问题,我想初始化根的内部“tree_t”-inside-。它首先将所有内部“tree_t”的代码流弄乱了,然后将它们全部放入我的根目录中。我只是想让我的代码更干净、更易于概览和维护:)
猜你喜欢
  • 1970-01-01
  • 2021-10-27
  • 1970-01-01
  • 2018-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-16
相关资源
最近更新 更多