2015-06-11 102 views
2

我對c和C++非常陌生。我是白天的c#程序員,很少再使用數組。不過,我正在開發一個C++項目。我想創建一個菜單,一個GUI,將類似於:使用多維數組創建菜單

Menu Item 1 
    Item 1.1 
     Item 1.1.1 
     Item 1.1.2 
    Item 1.2 
     Item 1.2.1 
     Item 1.2.2 
Menu Item 2 
    Item 2.1 
     Item 2.1.1 
     Item 2.1.2 
    Item 2.2 
     Item 2.2.1 
     Item 2.2.2 

我能創造一個二維版本,但要建立一個三維版本。任何幫助是極大的讚賞。

這裏是我的2維版本:

const char* menu[2][4] = 
    { 
     {"Menu Item 1", "1.1 Item", "1.2 Item", "1.3 Item"}, 
     {"Menu Item 2", "2.1 Item", "2.2 Item", "2.3 Item"} 

    }; 


    for(int i = 0; i < NUMOFCHO; i++){ 

     printf("%s\n\r", menu[i][0]); 

     for(int k = 1; k < NUMOFITEMS; k++){ 
      printf("--%s\n\r", menu[i][k]);   

     } 
    } 
+0

我不認爲靜態數組是這種情況下使用的寫入數據結構。看看[std :: list](http://en.cppreference.com/w/cpp/container/list)和[std :: vector](http://en.cppreference.com/w/cpp /容器/載體)。 –

+0

你也可以使用樹形數據結構來組織你的菜單和項目,但是這將需要遞歸搜索來訪問數據 – Ediac

+1

@AustinBrunkhorst我不認爲靜態數組* * *; *) – twalberg

回答

2

如果需要靈活性,靜態數組可能不是最好的數據結構。

我建議使用動態大小的容器,如std::vector

您經常會看到像這樣的菜單結構,使用「樹」類型的層次結構,其中菜單的每個級別都有一個名稱及其子項(子菜單項)。以下是你如何着手使用這些概念。

struct MenuItem 
{ 
    // name of the menu item 
    std::string name; 

    // sub menu items 
    std::vector<MenuItem> children; 
}; 

遍歷菜單最容易使用遞歸,下面是一個簡單的例子。

void printMenu(const std::vector<MenuItem> &menu, int level) 
{ 
    // string containing tabs depending on the level of the menu 
    std::string prefix(level, '\t'); 

    // we're visiting the next menu level 
    ++level; 

    // iterate over this level of the menu 
    for (const auto &item : menu) 
    { 
     // display the item name 
     std::cout << prefix << item.name << std::endl; 

     // visit this level's children 
     printMenu(item.children, level); 
    } 
} 

下面介紹如何初始化提供的示例菜單結構。

std::vector<MenuItem> menu 
{ 
    { 
     { 
      { "Menu Item 1" }, 
      { 
       { 
        { "Item 1.1" }, 
        { 
         { 
          { "Item 1.1.1" }, 
          { {} } 
         }, 
         { 
          { "Item 1.1.2" }, 
          { {} } 
         } 
        } 
       }, 
       { 
        { "Item 1.2" }, 
        { 
         { 
          { "Item 1.2.1" }, 
          { {} } 
         }, 
         { 
          { "Item 1.2.2" }, 
          { {} } 
         } 
        } 
       }, 
      } 
     }, 
     { 
      { "Menu Item 2" }, 
      { 
       { 
        { "Item 2.1" }, 
        { 
         { 
          { "Item 2.1.1" }, 
          { {} } 
         }, 
         { 
          { "Item 2.1.2" }, 
          { {} } 
         } 
        } 
       }, 
       { 
        { "Item 2.2" }, 
        { 
         { 
          { "Item 2.2.1" }, 
          { {} } 
         }, 
         { 
          { "Item 2.2.2" }, 
          { {} } 
         } 
        } 
       }, 
      } 
     } 
    } 
}; 

要訪問特定的菜單項,您可以執行以下操作。

// Item 2.2.1 
std::string item = menu[ 1 ].children[ 1 ].children[ 0 ].name; 

甚至在結構初始化後添加一個項目。

menu[ 1 ].children[ 1 ].children.push_back({ 
    { "Testing" }, // name 
    { {} }   // children 
}); 

/* 
    Menu Item 1 
     Item 1.1 
       Item 1.1.1 
       Item 1.1.2 
     Item 1.2 
       Item 1.2.1 
       Item 1.2.2 
    Menu Item 2 
     Item 2.1 
       Item 2.1.1 
       Item 2.1.2 
     Item 2.2 
       Item 2.2.1 
       Item 2.2.2 
       Testing 
*/ 

你可以找到一個完整的例子here

1

陣列通常是嵌套的菜單中選擇不當。

以我的經驗菜單通常連接在一起,而不是一個數組:

Menu1 
    Item1 --> Menu1.1 
    | 
    v 
    Item2 --> Menu1.2 
    | 
    v 
    Item3 

我通常我有一個菜單包含項目。每個項目可以指向另一個子菜單。這允許項目沒有子菜單的情況。

0

我有一個解決方案,我在C中實現了一段時間。它的設置深度達到8層,但技術上沒有深度限制。它適用於嵌入式應用程序,因此它爲所有應用程序使用靜態內存,但可以輕鬆修改它以使用動態分配。您可以在my github page上找到源代碼,但我會在這裏稍微介紹一下。

菜單是圍繞作爲二叉樹(b-tree)實現的k-tree樹(k-tree)構建的。與常規k-樹不同,節點可以有多個孩子,這對於菜單系統來說是有意義的,因爲系統是內存和資源受限的,二叉樹更多。它允許靜態分配節點,而不使用固定大小的數組來存儲子節點。這個實現起初有點令人困惑,但實際上在一些資源敏感的系統(如Linux內核)中使用。

定期K-樹表示爲:

struct t { 
    int n; 
    int numKids; 
    struct t **kids; 
} 

和結果樹struture樣子:

TOP 
    | 
    [ list of kids ] 
    |  |  | 
    k1 k2  k3 
    | 
    | 
    [list of kids] 
    |  |  | 
    k1.1 k1.2 k1.3 

,如果你有事情malloc之類,但一個嵌入式 系統上這表示工作在使用malloc的時候,如果你想添加另一個孩子,除了必須在結構中記錄數組中孩子的數量 之外,還可以使用有限長度的靜態數組。

相反,下面的結構被用於:

struct t { 
    int n; 
    struct t *firstChildNode; 
    struct t *firstSiblingNode; 
    struct t *trueParentNode; 
    struct t *fakeParentNode; 
}; 

和樹表示看起來像:


[TOP NODE ] <- 
    ----------- \ 
    ^|^  \ 
    || |   \ fP 
    || |tP, fP  \ 
    || |_______ fS \__________  fS 
    || [NODE 2]------>[NODE 2.1]---------->NULL 
    || ------- <------ --------- 
    ||  |  tP  | 
    ||  |fC   |fC 
    ||fC  V    V 
    ||  NULL   NULL 
    || 
    |tP, fP 
    || 
    |V_____ 
    [NODE1] 
    ------- 
    ^
    |tP, fp 
    |_________ fS 
    [NODE 1.1] -----> NULL 
     | 
     |fC 
     | 
     V 
     NULL 

其中:

  • FP = fakeParentNode - 這個指針用於確定實際的父親,因爲它會出現在經典的k-tree樹中。
  • tP = trueParentNode - 該指針用於顯示此節點實際連接的位置,因爲它可以是另一個 節點或同級的子節點。
  • fC = firstChildNode - 指向第一個子節點的指針。
  • fS = firstSiblingNode - 指向第一個兄弟節點的指針。

從任何一個節點,你只能訪問第一個孩子或第一個兄弟,使之成爲一個經典的B樹。從第一個孩子,你可以訪問它的 兒童和兄弟姐妹。同樣的第一個兄弟指針。這樣所有的節點都被存儲在一個列表中,該列表可以包含靜態分配的 結構。真假父指針用於輕鬆追蹤每個子節點的 祖先。假父指針用於在此b樹上映射經典k樹結構。

有幾個優點這看似混亂的方法:

  • 每個節點是一個固定的大小,並且可以靜態地分配。
  • 表示子節點不需要數組。
  • 許多算法可以很容易地表達,因爲它只是一個B樹。

菜單的實現是有限的,因爲刪除節點的功能是不存在的,因爲這似乎並沒有成爲一個菜單的必要功能。添加菜單或菜單項非常簡單。見menu_top.c例如

這種設計的https://blog.mozilla.org/nnethercote/2012/03/07/n-ary-trees-in-c/

代碼ktree.c啓發:

/* Includes ------------------------------------------------------------------*/ 
#include "ktree.h" 
#include "qp_port.h"          /* for QP support */ 
#include "project_includes.h" 

/* Compile-time called macros ------------------------------------------------*/ 
Q_DEFINE_THIS_FILE     /* For QSPY to know the name of this file */ 
DBG_DEFINE_THIS_MODULE(DBG_MODL_DBG);/* For debug system to ID this module */ 

/* Private typedefs ----------------------------------------------------------*/ 
/* Private defines -----------------------------------------------------------*/ 
/* Private macros ------------------------------------------------------------*/ 
/* Private variables and Local objects ---------------------------------------*/ 
static treeNode_t Menu1; 
char *const Menu1Txt = "Menu 1"; 

    static treeNode_t MenuItem1; 
    char *const MenuItem1_1Txt = "Menu Item 1.1"; 

    static treeNode_t MenuItem2; 
    char *const MenuItem1_2Txt = "Menu Item 1.2"; 

    static treeNode_t MenuItem3; 
    char *const MenuItem1_3Txt = "Menu Item 1.3"; 

    static treeNode_t MenuItem4; 
    char *const MenuItem1_4Txt = "Menu Item 1.4"; 

static treeNode_t Menu2; 
char *const Menu2Txt = "Menu 2"; 

    static treeNode_t MenuItem2_1; 
    char *const MenuItem2_1Txt = "Menu Item 2.1"; 

    static treeNode_t MenuItem2_2; 
    char *const MenuItem2_2Txt = "Menu Item 2.2"; 

    static treeNode_t MenuItem2_3; 
    char *const MenuItem2_3Txt = "Menu Item 2.3"; 

    static treeNode_t MenuItem2_4; 
    char *const MenuItem2_4Txt = "Menu Item 2.4"; 

static treeNode_t Menu3; 
char *const Menu3Txt = "Menu 3"; 

    static treeNode_t Menu3_1; 
    char *const Menu3_1Txt = "Menu 3_1"; 

     static treeNode_t MenuItem3_1_1; 
     char *const MenuItem3_1_1Txt = "Menu Item 3.1.1"; 

     static treeNode_t MenuItem3_1_2; 
     char *const MenuItem3_1_2Txt = "Menu Item 3.1.2"; 

     static treeNode_t MenuItem3_1_3; 
     char *const MenuItem3_1_3Txt = "Menu Item 3.1.3"; 

     static treeNode_t MenuItem3_1_4; 
     char *const MenuItem3_1_4Txt = "Menu Item 3.1.4"; 

    static treeNode_t Menu3_2; 
    char *const Menu3_2Txt = "Menu 3_2"; 

     static treeNode_t MenuItem3_2_1; 
     char *const MenuItem3_2_1Txt = "Menu Item 3.2.1"; 

     static treeNode_t MenuItem3_2_2; 
     char *const MenuItem3_2_2Txt = "Menu Item 3.2.2"; 

     static treeNode_t MenuItem3_2_3; 
     char *const MenuItem3_2_3Txt = "Menu Item 3.2.3"; 

     static treeNode_t MenuItem3_2_4; 
     char *const MenuItem3_2_4Txt = "Menu Item 3.2.4"; 

    static treeNode_t Menu3_3; 
    char *const Menu3_3Txt = "Menu 3_3"; 

     static treeNode_t Menu3_3_1; 
     char *const Menu3_3_1Txt = "Menu 3_3_1"; 

     static treeNode_t MenuItem3_3_1_1; 
     char *const MenuItem3_3_1_1Txt = "Menu Item 3.3.1.1"; 

     static treeNode_t MenuItem3_3_1_2; 
     char *const MenuItem3_3_1_2Txt = "Menu Item 3.3.1.2"; 

     static treeNode_t MenuItem3_3_1_3; 
     char *const MenuItem3_3_1_3Txt = "Menu Item 3.3.1.3"; 

     static treeNode_t Menu3_3_2; 
     char *const Menu3_3_2Txt = "Menu 3_3_2"; 

     static treeNode_t MenuItem3_3_2_1; 
     char *const MenuItem3_3_2_1Txt = "Menu Item 3.3.2.1"; 

     static treeNode_t MenuItem3_3_2_2; 
     char *const MenuItem3_3_2_2Txt = "Menu Item 3.3.2.2"; 

     static treeNode_t MenuItem3_3_2_3; 
     char *const MenuItem3_3_2_3Txt = "Menu Item 3.3.2.3"; 

     static treeNode_t Menu3_3_3; 
     char *const Menu3_3_3Txt = "Menu 3_3_3"; 

     static treeNode_t MenuItem3_3_3_1; 
     char *const MenuItem3_3_3_1Txt = "Menu Item 3.3.3.1"; 

     static treeNode_t MenuItem3_3_3_2; 
     char *const MenuItem3_3_3_2Txt = "Menu Item 3.3.3.2"; 

     static treeNode_t MenuItem3_3_3_3; 
     char *const MenuItem3_3_3_3Txt = "Menu Item 3.3.3.3"; 

     static treeNode_t Menu3_3_4; 
     char *const Menu3_3_4Txt = "Menu 3_3_4"; 

     static treeNode_t MenuItem3_3_4_1; 
     char *const MenuItem3_3_4_1Txt = "Menu Item 3.3.4.1"; 

     static treeNode_t MenuItem3_3_4_2; 
     char *const MenuItem3_3_4_2Txt = "Menu Item 3.3.4.2"; 

     static treeNode_t MenuItem3_3_4_3; 
     char *const MenuItem3_3_4_3Txt = "Menu Item 3.3.4.3"; 

/* Private function prototypes -----------------------------------------------*/ 
/* Private functions ---------------------------------------------------------*/ 

/******************************************************************************/ 
void KTREE_nodeCtor(treeNode_t *node) 
{ 
    /* Initialize the root node */ 
    node->firstChildNode = NULL; 
    node->firstSiblingNode = NULL; 
    node->fakeParentNode = NULL; 
    node->trueParentNode = NULL; 
} 

/******************************************************************************/ 
uint8_t KTREE_findDepth(treeNode_t *node, uint8_t currDepth) 
{ 
    /* Make sure the node is not null. If this happens, it's a bug. */ 
    if (NULL == node) { 
     ERR_printf("!!!Node is null\n"); 
     return(0); 
    } 

    if (NULL == node->fakeParentNode) { 
     /* If no fake parents exist, this is the top. Return the calculated 
     * depth */ 
     return(currDepth); 
    } else { 
     /* Parent exists; recurse after incrementing the depth */ 
     return (KTREE_findDepth(node->fakeParentNode, ++currDepth)); 
    } 
} 

/******************************************************************************/ 
void KTREE_addChild(
     treeNode_t *childToAdd, 
     treeNode_t *trueParentNode, 
     treeNode_t *fakeParentNode 
) 
{ 
    /* Find the parent node */ 
    if (NULL == trueParentNode) { 
     childToAdd->trueParentNode = NULL; 
     childToAdd->fakeParentNode = NULL; 
     return; 
    } else if (NULL == trueParentNode->firstChildNode) { 

     /* If the node where we want to add a child currently has no children, 
     * add the child node here to the childNode field. */ 
     trueParentNode->firstChildNode = childToAdd; 
//  dbg_slow_printf("whereToAddChild=0x%08x\n", (uint32_t)whereToAddChild); 
    } else { 
     /* Otherwise, add a sibling to the child of this node */ 
     KTREE_addNextSibling(childToAdd, trueParentNode->firstChildNode, fakeParentNode); 
//  dbg_slow_printf("Adding a sibling to the child of node '%s'\n", trueParentNode->text); 
    } 
} 

/******************************************************************************/ 
void KTREE_addNextSibling(
     treeNode_t *siblingToAdd, 
     treeNode_t *trueParentNode, 
     treeNode_t *fakeParentNode 
) 
{ 
    if(NULL == trueParentNode->firstSiblingNode) { 
     /* In the node being added, set its parents. */ 
     siblingToAdd->fakeParentNode = fakeParentNode; 
     siblingToAdd->trueParentNode = trueParentNode; 

     /* Set the empty firstSiblingNode pointer to new node being added. */ 
     trueParentNode->firstSiblingNode = siblingToAdd; 
//  dbg_slow_printf("Added node '%s' as a sibling to node '%s'\n", siblingToAdd->text, trueParentNode->text); 
    } else { 
//  dbg_slow_printf("Sibling '%s' already exists here. Trying next one\n", trueParentNode->text); 
     KTREE_addNextSibling(siblingToAdd, trueParentNode->firstSiblingNode, fakeParentNode); 
    } 
} 

/******************************************************************************/ 
void KTREE_printTree(treeNode_t *node, uint8_t level) 
{ 
    if (NULL == node) { 
//  dbg_slow_printf("Node at level %d is null, not printing\n", level); 
     return; 
    } else { 
     KTREE_printNode(node, level); 
    } 

    if (NULL != node->firstChildNode) { 
//  dbg_slow_printf("Child exists, descending one level\n"); 
     KTREE_printTree(node->firstChildNode, level+1); 
    } 

    if (NULL != node->firstSiblingNode) { 
//  dbg_slow_printf("Sibling exits, moving right\n"); 
     KTREE_printTree(node->firstSiblingNode, level); 
    } 

} 

/******************************************************************************/ 
void KTREE_printNode(treeNode_t *node, uint8_t level) 
{ 
    for (uint8_t i = 0; i < level; i++) { 
     printf("*--"); 
    } 
    printf("NodeText: %s\n", node->text); 
    for (uint8_t i = 0; i < level; i++) { 
     printf("*--"); 
    } 
    printf("True Parent pointer: 0x%08x\n", (uint32_t)node->trueParentNode); 
    for (uint8_t i = 0; i < level; i++) { 
     printf("*--"); 
    } 
    printf("Fake Parent pointer: 0x%08x\n", (uint32_t)node->fakeParentNode); 
    for (uint8_t i = 0; i < level; i++) { 
     printf("*--"); 
    } 
    printf("First Sibling pointer: 0x%08x\n", (uint32_t)node->firstSiblingNode); 
    for (uint8_t i = 0; i < level; i++) { 
     printf("*--"); 
    } 
    printf("First Child pointer: 0x%08x\n", (uint32_t)node->firstChildNode); 
} 

/******************************************************************************/ 
void KTREE_fakeMenuTest(void) 
{ 
    /* First Node */ 
    KTREE_nodeCtor(&Menu1); 
    Menu1.text = Menu1Txt; 

     /* Add a child node */ 
     KTREE_nodeCtor(&MenuItem1); 
     MenuItem1.text = MenuItem1_1Txt; 
     KTREE_addChild(&MenuItem1, &Menu1, &Menu1); 

     /* Add siblings to that child node */ 
     KTREE_nodeCtor(&MenuItem2); 
     MenuItem2.text = MenuItem1_2Txt; 
     KTREE_addChild(&MenuItem2, &Menu1, &Menu1); 

     KTREE_nodeCtor(&MenuItem3); 
     MenuItem3.text = MenuItem1_3Txt; 
     KTREE_addChild(&MenuItem3, &Menu1, &Menu1); 

     KTREE_nodeCtor(&MenuItem4); 
     MenuItem4.text = MenuItem1_4Txt; 
     KTREE_addChild(&MenuItem4, &Menu1, &Menu1); 

    /* Add another node at top level - it gets added as a sibling to the first node */ 
    KTREE_nodeCtor(&Menu2); 
    Menu2.text = Menu2Txt; 
    KTREE_addNextSibling(&Menu2, &Menu1, NULL); 

     /* Add menu items to it as children */ 
     KTREE_nodeCtor(&MenuItem2_1); 
     MenuItem2_1.text = MenuItem1_1Txt; 
     KTREE_addChild(&MenuItem2_1, &Menu2, &Menu2); 

     /* Add siblings to that child node */ 
     KTREE_nodeCtor(&MenuItem2_2); 
     MenuItem2_2.text = MenuItem2_2Txt; 
     KTREE_addChild(&MenuItem2_2, &Menu2, &Menu2); 

     KTREE_nodeCtor(&MenuItem2_3); 
     MenuItem2_3.text = MenuItem2_3Txt; 
     KTREE_addChild(&MenuItem2_3, &Menu2, &Menu2); 

     KTREE_nodeCtor(&MenuItem2_4); 
     MenuItem2_4.text = MenuItem2_4Txt; 
     KTREE_addChild(&MenuItem2_4, &Menu2, &Menu2); 

    /* Add another node at top level - it gets added as a sibling to the first node */ 
    KTREE_nodeCtor(&Menu3); 
    Menu3.text = Menu3Txt; 
    KTREE_addNextSibling(&Menu3, &Menu1, NULL); 

     /* Add submenu nodes - it gets added as a sibling to the first node */ 
     KTREE_nodeCtor(&Menu3_1); 
     Menu3_1.text = Menu3_1Txt; 
     KTREE_addChild(&Menu3_1, &Menu3, &Menu3); 

     /* Add menu items to it as children */ 
     KTREE_nodeCtor(&MenuItem3_1_1); 
     MenuItem3_1_1.text = MenuItem3_1_1Txt; 
     KTREE_addChild(&MenuItem3_1_1, &Menu3_1, &Menu3_1); 

     KTREE_nodeCtor(&MenuItem3_1_2); 
     MenuItem3_1_2.text = MenuItem3_1_2Txt; 
     KTREE_addChild(&MenuItem3_1_2, &Menu3_1, &Menu3_1); 

     KTREE_nodeCtor(&MenuItem3_1_3); 
     MenuItem3_1_3.text = MenuItem3_1_3Txt; 
     KTREE_addChild(&MenuItem3_1_3, &Menu3_1, &Menu3_1); 

     KTREE_nodeCtor(&MenuItem3_1_4); 
     MenuItem3_1_4.text = MenuItem3_1_4Txt; 
     KTREE_addChild(&MenuItem3_1_4, &Menu3_1, &Menu3_1); 

     KTREE_nodeCtor(&Menu3_2); 
     Menu3_2.text = Menu3_2Txt; 
     KTREE_addChild(&Menu3_2, &Menu3, &Menu3); 

     /* Add menu items to it as children */ 
     KTREE_nodeCtor(&MenuItem3_2_1); 
     MenuItem3_2_1.text = MenuItem3_2_1Txt; 
     KTREE_addChild(&MenuItem3_2_1, &Menu3_2, &Menu3_2); 

     KTREE_nodeCtor(&MenuItem3_2_2); 
     MenuItem3_2_2.text = MenuItem3_2_2Txt; 
     KTREE_addChild(&MenuItem3_2_2, &Menu3_2, &Menu3_2); 

     KTREE_nodeCtor(&MenuItem3_2_3); 
     MenuItem3_2_3.text = MenuItem3_2_3Txt; 
     KTREE_addChild(&MenuItem3_2_3, &Menu3_2, &Menu3_2); 

     KTREE_nodeCtor(&MenuItem3_2_4); 
     MenuItem3_2_4.text = MenuItem3_2_4Txt; 
     KTREE_addChild(&MenuItem3_2_4, &Menu3_2, &Menu3_2); 

     KTREE_nodeCtor(&Menu3_3); 
     Menu3_3.text = Menu3_3Txt; 
     KTREE_addChild(&Menu3_3, &Menu3, &Menu3); 

     KTREE_nodeCtor(&Menu3_3_1); 
     Menu3_3_1.text = Menu3_3_1Txt; 
     KTREE_addChild(&Menu3_3_1, &Menu3_3, &Menu3_3); 

      /* Add menu items to it as children */ 
      KTREE_nodeCtor(&MenuItem3_3_1_1); 
      MenuItem3_3_1_1.text = MenuItem3_3_1_1Txt; 
      KTREE_addChild(&MenuItem3_3_1_1, &Menu3_3_1, &Menu3_3_1); 

      KTREE_nodeCtor(&MenuItem3_3_1_2); 
      MenuItem3_3_1_2.text = MenuItem3_3_1_2Txt; 
      KTREE_addChild(&MenuItem3_3_1_2, &Menu3_3_1, &Menu3_3_1); 

      KTREE_nodeCtor(&MenuItem3_3_1_3); 
      MenuItem3_3_1_3.text = MenuItem3_3_1_3Txt; 
      KTREE_addChild(&MenuItem3_3_1_3, &Menu3_3_1, &Menu3_3_1); 

     KTREE_nodeCtor(&Menu3_3_2); 
     Menu3_3_2.text = Menu3_3_2Txt; 
     KTREE_addChild(&Menu3_3_2, &Menu3_3, &Menu3_3); 

      /* Add menu items to it as children */ 
      KTREE_nodeCtor(&MenuItem3_3_2_1); 
      MenuItem3_3_2_1.text = MenuItem3_3_2_1Txt; 
      KTREE_addChild(&MenuItem3_3_2_1, &Menu3_3_2, &Menu3_3_2); 

      KTREE_nodeCtor(&MenuItem3_3_2_2); 
      MenuItem3_3_2_2.text = MenuItem3_3_2_2Txt; 
      KTREE_addChild(&MenuItem3_3_2_2, &Menu3_3_2, &Menu3_3_2); 

      KTREE_nodeCtor(&MenuItem3_3_2_3); 
      MenuItem3_3_2_3.text = MenuItem3_3_2_3Txt; 
      KTREE_addChild(&MenuItem3_3_2_3, &Menu3_3_2, &Menu3_3_2); 

     KTREE_nodeCtor(&Menu3_3_3); 
     Menu3_3_3.text = Menu3_3_3Txt; 
     KTREE_addChild(&Menu3_3_3, &Menu3_3, &Menu3_3); 

      /* Add menu items to it as children */ 
      KTREE_nodeCtor(&MenuItem3_3_3_1); 
      MenuItem3_3_3_1.text = MenuItem3_3_3_1Txt; 
      KTREE_addChild(&MenuItem3_3_3_1, &Menu3_3_3, &Menu3_3_3); 

      KTREE_nodeCtor(&MenuItem3_3_3_2); 
      MenuItem3_3_3_2.text = MenuItem3_3_3_2Txt; 
      KTREE_addChild(&MenuItem3_3_3_2, &Menu3_3_3, &Menu3_3_3); 

      KTREE_nodeCtor(&MenuItem3_3_3_3); 
      MenuItem3_3_3_3.text = MenuItem3_3_3_3Txt; 
      KTREE_addChild(&MenuItem3_3_3_3, &Menu3_3_3, &Menu3_3_3); 

     KTREE_nodeCtor(&Menu3_3_4); 
     Menu3_3_4.text = Menu3_3_4Txt; 
     KTREE_addChild(&Menu3_3_4, &Menu3_3, &Menu3_3); 

      /* Add menu items to it as children */ 
      KTREE_nodeCtor(&MenuItem3_3_4_1); 
      MenuItem3_3_4_1.text = MenuItem3_3_4_1Txt; 
      KTREE_addChild(&MenuItem3_3_4_1, &Menu3_3_4, &Menu3_3_4); 

      KTREE_nodeCtor(&MenuItem3_3_4_2); 
      MenuItem3_3_4_2.text = MenuItem3_3_4_2Txt; 
      KTREE_addChild(&MenuItem3_3_4_2, &Menu3_3_4, &Menu3_3_4); 

      KTREE_nodeCtor(&MenuItem3_3_4_3); 
      MenuItem3_3_4_3.text = MenuItem3_3_4_3Txt; 
      KTREE_addChild(&MenuItem3_3_4_3, &Menu3_3_4, &Menu3_3_4); 

    KTREE_printTree(&Menu1, 0); 
} 

代碼ktree.h:

#ifndef KTREE_H_ 
#define KTREE_H_ 

#ifdef __cplusplus 
extern "C" { 
#endif 

/* Includes ------------------------------------------------------------------*/ 
#include "stm32f4xx.h"         /* For STM32F4 support */ 
#include "Shared.h" 

/* Exported defines ----------------------------------------------------------*/ 
/* Exported types ------------------------------------------------------------*/ 

typedef void (*pMenuFunction) (
     const char* dataBuf, 
     uint16_t dataLen, 
     CBMsgRoute dst 
); 

typedef struct treeNode { 
    struct treeNode *fakeParentNode; 
    struct treeNode *trueParentNode; 
    struct treeNode *firstChildNode; 
    struct treeNode *firstSiblingNode; 
    char *text; 
    char *selector; 
    pMenuFunction actionToTake; 
} treeNode_t; 


/* Exported macros -----------------------------------------------------------*/ 
/* Exported constants --------------------------------------------------------*/ 
/* Exported variables --------------------------------------------------------*/ 
/* Exported functions --------------------------------------------------------*/ 


/** 
* @brief Initialize the menu memory space with the contents of the menu 
* 
* This function initializes the menu application. 
* 
* @param [in] iBus: I2C_Bus_t identifier for I2C bus to initialize 
* @arg 
* @return: None 
*/ 
void KTREE_nodeCtor(treeNode_t *node); 

/** 
* @brief Counts the depth (via fakeParentNode pointer) of the passed in 
* pointer to a node. 
* 
* Recursive function that counts it's own depth by traversing the tree up via 
* the fakeParentNode pointer and counting. 
* 
* @param [in] *node: treeNode_t pointer to the node 
* @parem [in] currDepth: uint8_t value of the current depth. Unless you want 
* to for some reason offset where you start counting from, pass in 0 here. 
* @return: uint8_t depth of the current node. 
*/ 
uint8_t KTREE_findDepth(treeNode_t *node, uint8_t currDepth); 

/** 
* @brief Adds a child node to a passed in parent node. 
* 
* This function adds a child node to a passed in parent node after doing some 
* initial checking of both nodes. 
* 
* @param [in] childNode: treeNode_t* pointer to the node to be added as a child 
* @parem [in] trueParentNone: treeNode_t* pointer to the node that the child 
* node will be directly attached to because it may appear as a sibling node or 
* an actual child node. 
* @parem [in] fakeParentNone: treeNode_t* pointer to the node that indicates 
* the "regular" tree depth of this child node. 
* @return: None 
*/ 
void KTREE_addChild(
     treeNode_t *childToAdd, 
     treeNode_t *trueParentNode, 
     treeNode_t *fakeParentNode 
); 

/** 
* @brief Adds a sibling node to a passed in parent node. 
* 
* This function adds a sibling node to a passed in parent node by checking if 
* the child node of the trueParentNode is used up and if it is, it recursively 
* calls itself with the used node as a trueParentNode parameter until it finds 
* the last sibling. It then adds the sibling node there and sets the 
* fakeParentNode. 
* 
* @param [in] childNode: treeNode_t* pointer to the node to be added as a child 
* @parem [in] trueParentNone: treeNode_t* pointer to the node that the child 
* node will be directly attached to because it may appear as a sibling node or 
* an actual child node. 
* @parem [in] fakeParentNone: treeNode_t* pointer to the node that indicates 
* the "regular" tree depth of this child node. 
* @return: None 
*/ 
void KTREE_addNextSibling(
     treeNode_t *siblingToAdd, 
     treeNode_t *trueParentNode, 
     treeNode_t *fakeParentNode 
); 

void KTREE_printTree(treeNode_t *node, uint8_t level); 

void KTREE_printNode(treeNode_t *node, uint8_t level); 

void KTREE_fakeMenuTest(void); 

我不想要在這裏發佈剩餘的代碼(這已經很多了),但是看看我的鏈接中的其他目錄,看看它是如何使用的。 menu.c/h使用ktree來設置一個實際的菜單並使用它。