Инициализация константы struct, содержащая указатель на собственный тип

У меня такая структура:

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, поэтому не стесняйтесь редактировать или спрашивать, не смог ли я сделать себя ясно 🙂

Вариант 1a – массив

Структура вашего дерева кажется зацикленной довольно линейной формой, но вы можете использовать массив, чтобы обойти проблему:

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

Вариант 1b – дерево, построенное из массива

Или задана структура дерева двоичного поиска:

 #include  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)]]] 

Вариант 2а – Смешанные литералы C99

С C99 и составными литералами вы можете написать:

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

Я не уверен, что это parsingчиво, но он компилируется. В целом, я предпочитаю нотацию массива.

Это основной ответ, который 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 }; 

GCC (i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (на основе Apple Inc. build 5658) (LLVM build 2336.11.00)) в Mac OS X 10.8.4 жалуется:

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

Там, где отмечена строка 47, это конец массива структур. Возможно, мне не хватает чего-то очевидного. Я попробовал &(test_t[]){ ... }[0], но получил такое же предупреждение.

Я совсем не уверен, как вы могли бы сказать, что конкретный указатель был указателем на начало массива, а не на один элемент tree_t , если вы не добавите другое поле для указания разницы (или поле b каким-то образом закодировано для указания является ли a указателем на один элемент или указателем на массив).

Я не думаю, что этот код:

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

будет успешно инициализировать корень tree_t.

{} Предназначен для инициализации структуры, а не указателя. Но root-> a определяется как указатель. Вы можете получить ошибку компиляции.

Ветвь может быть определена и инициализирована вне указанного выше кода, и установите для нее указатель root-> a point.