简体   繁体   中英

Initializing constant struct containing pointer to its own type

I'm having a struct like this:

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

Which, at the moment, I am initializing in this way:

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

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

Now, it annoys me that I have to initialize the lower branches before the root because the complete structure is quite large, with the branches having branches on their own, making my code hard to manage.

What I would like to do is something like this:

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
};

How can I achieve an initialization like this?

The main reason I want to do this is to greatly enhance the overview of my code and immediately visually see the structure of the "Tree".

Note that the structure of the complete "Tree" is know from the start which is why I consider the structure constant. The value b however may be changed at any time.

I am quite new to the C language and this is my first post on SO, so feel free to edit or ask if I havent been able to make myself clear :)

Option 1a — An array

Your 'tree' structure seems to be stuck with a rather linear shape, but you can use an array to get around the problem:

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

Option 1b — A tree built from an array

Or given a binary search tree structure:

#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;
}

This produces the output:

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

Option 2a — C99 compound literals

With C99 and compound literals, you can write:

#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
};

I'm not sure that it is legible, but it compiles OK. On the whole, I prefer the array notation, though.

This is the primary answer that the OP wanted/used.


Option 2b — Failed attempt to get array into initialization

Attempting to adapt the revised data structure in question (bearing in mind that the 'tree' that can be created with the data structure is only a singly-linked list), you can almost (but not quite) do it with this:

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
};

GCC (i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)) on Mac OS X 10.8.4 complains:

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

Where line 47 is marked — it is the end of the array of structures. I may be missing something obvious. I did try &(test_t[]){ ... }[0], but that got the same warning.

I'm not at all sure how you could tell that the particular pointer was a pointer to the start of an array rather than a single tree_t element, unless you add another field to indicate the difference (or the b field is somehow encoded to indicate whether the a is a pointer to a single item or a pointer to an array).

I do not think this code:

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

will successfully initialize the tree_t root.

The {} is for initializing a struct, not a pointer. But root->a is defined to be a pointer. You might get compiling error by this.

The branch could be defined and initialized outside the above code, and set the pointer root->a point to it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM