【C】【OOPC】【链表】一份在递归函数中可用的 List 实现 - 实例:深度优先遍历算法遍历树结构获取树末端节点的值

本文介绍C语言中树数据结构的实现与深度优先遍历算法,展示如何使用链表改进算法,确保内存有效管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



提醒: 这篇博客很长,代码有点儿长,内容有点儿难度


全文要点:

  • 树数据结构
  • 深度优先算法(遍历)
  • 链表
  • 内存管理
  • OOPC

为了方便已经将本文用到的所有代码上传到 GitHub ? 上。


1 树 - 数据结构

1.1 树 - 图示

这里的树数据结构使用的是 TR098 model 作为实例。如果读者不知到 tr098 model 是什么也没有关系,因为这里和具体细节无关,只是取其结构用于测试罢了。

                              +--------------------------+
                              |  InternetGatewayDevice   |
                              +---------O----------------+
                                      / | └----------.
                ┌--------------------┘  |             `---------┐
           +--------O---+        +------O----+          +-------O---+
           | DeviceInfo |        | LANDevice |          | WANDevice |
           +------O-----+        +-----O-----+          +------O-----+
           ____ /   \__                |                       |
+---------O----+  +----`o-----+     +---O--+               ...略...
| Manufacturer |  | ModelName |     |   1  |
+--------------+  +-----------+     +---O--+
                            ┌----------┘ └------------┐
                     +------O------------+   +--------O-------------------+
                     | WLANConfiguration |   | LANEthernetInterfaceConfig |
                     +------O------------+   +--------O-------------------+
                            |                         |
                        +---O--+                   ...略...
                        |   1  |
                        +---O--+
                ______/ /  |  \
      ┌--------┘   ____/   |   \_____
 +----O---+  +----O--+  ...略...  +---O---+
 | Enable |  | BSSID |            | Stats |
 +--------+  +-------+            +--O----+
                           ,________/ \_____
                  +-------O-----+   +-------`o-------+
                  | EorrorsSent |   | ErrorsReceived |
                  +-------------+   +----------------+

可以看到,上面上下都有 +---O---+ 的即是树 Node,而底部 +-----+ 没有 O 就是到底了,没有子节点(Parameter)。
每个 Parameter 都含有一个 Value,这里为了简单起见,后面获取 Value 就使用这个节点的名称 name 即可。

1.2 抽象程序语言内的树结构

使用 C 语言表示这个树结构的话,会是这样子:

        
   O├- NULL
    |
    \/
    O├-->  O├-->  O├-> NULL
     |             |
    \/            \/
    O├-> NULL     O├--> O├---> O├->  O├-> NULL
     |
    \/
    NULL
   

注:该图示和上一图没有对应关系。

可以看到,我在这里使用了右指针形成链表来存储树结构的多个子节点信息。
第一个子节点可以通过父节点直接访问到,该父节点剩下的子节点需要通过该父节点的第一个子节点来访问。

1.3 树结构实现代码

节点的实现代码为:

typedef struct TreeNode_s {
    const char *name;
    struct TreeNode_s *p_brother;
    struct TreeNode_s *p_child;
} TreeNode_t;

前面说到,parameter 节点(最底部无子节点的节点)除了本身有的名称之外,还有一个 value。

可以联系电脑的文件夹存储结构。文件夹有下一层,文件没有下一层,文件就是 parameter
而文件除了文件名之外,还存有文件内的内容(value)。
而文件夹(node)也有文件夹名称(name),但是我们不会想要文件夹有内容(value),而是点进去文件夹,访问它的下一层(child)。

所以这里还需要为实现的代码添上获取内容的函数:

tree.h

typedef struct TreeNode_s {
    const char *name;
    struct TreeNode_s *p_brother;
    struct TreeNode_s *p_child;
    char* (*get_response_str)(struct TreeNode_s* me, const char *full_path, Action_t action);
} TreeNode_t;

注:Action_t action 在这里不会用上,将其当作是一个占位符就可以。忽略它的存在。

+-----O-----+  
| ModelName | 
+-----------+

为例;
如果获取它的值,我们得到 "ModelName"
但对于遍历到这 parameter 的调用方而言,假设我们不仅仅只是想要拿到 ModelName 这个值,我们还需要它的完整路径呢?
因为树结构,在其它路径的的底部,可能存在另外一个 ModelName 呢,所以这里获取的 parameter 的 value 之后,还需要构造 Response 给调用方。Response 是 XML 格式,含有完整路径信息,和 value 信息。
示例如下:

<ParameterValueStruct>
	<Name>InternetGatewayDevice.DeviceInfo.ModelName</Name>
	<Value xsi:type="xsd:string">ModelName</Value>
</ParameterValueStruct>

这就是我们调用 .get_response_str 函数得到的这个 node response 的完整“响应”字符串。

1.4 初始化和构造“树”-实例对象

main.c :

#include "utils.h"
#include "tree.h"

void setup(){
    build_tr098_model_tree(); 
}

int main(int argc, char const *argv[])
{
    setup();

    [...]

    return 0;
}

utils.h 参见 附 A
build_tr098_model_tree 函数参见 附 B

初始化树的 node 实例在 tree.c 中,为全局量。但是我们只需要一个树的 root 即可,所以这里参加 附 B,这个位置不贴代码



2 遍历树算法 - 深度优先算法递归实现

void DFS_traversal(
    const TreeNode_t * const p_root,
    const char *parent_path
){
    if (p_root==NULL || parent_path==NULL)
        exit(1);

    TreeNode_t *p_curr = p_root;
    char __parent_path[256] = {'\0'};
    do {
        if (!strlen(parent_path))
            sprintf(__parent_path, "%s", p_curr->name);
        else
            sprintf(__parent_path, "%s.%s", parent_path, p_curr->name);
 
        if (p_curr->p_child == NULL) {  // no child node
            printf("%s.%s\n", parent_path, p_curr->name);
        }
        else {
            DFS_traversal(p_curr->p_child, __parent_path);
            printf("↑ %-40s↑\n", p_curr->name);
        }
    }while(NULL != (p_curr = p_curr->p_brother));
}

测试:

main.c:

[...]

extern TreeNode_t *p_root;

[...]

int main(int argc, char const *argv[])
{
    setup();

    DFS_traversal(p_root, "");

    return 0;
}

输出:

InternetGatewayDevice.DeviceInfo.Manufacturer
InternetGatewayDevice.DeviceInfo.ManufacturerOUT
InternetGatewayDevice.DeviceInfo.ModelName
↑ DeviceInfo                              ↑
InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.Enable
InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.BSSID
InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.Stats.ErrorsSent
InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.Stats.ErrorsReceived
↑ Stats                                   ↑
↑ 1                                       ↑
↑ WLANConfiguration                       ↑
InternetGatewayDevice.LANDevice.1.LANEthernetInterfaceConfig.1.Name
InternetGatewayDevice.LANDevice.1.LANEthernetInterfaceConfig.1.Enable
↑ 1                                       ↑
↑ LANEthernetInterfaceConfig              ↑
↑ 1                                       ↑
↑ LANDevice                               ↑
↑ InternetGatewayDevice                   ↑

可以看到这里目前没有获取 parameter 的 Value,而是将遍历到的完整路径打印了出来。
这样便于一步步实现。



3 OOPC 链表实现

如果使用递归实现遍历算法的化,在 C 语言中,函数内创建了变量然后赋予一个有意义的值,在函数结束之后这个变量就会被销毁。
所以上面的代码,除了打印出来变量之外没有什么作用。

如果我在 main 创建了一个超大的数组,然后传递进入函数,该函数重复传递这个数组指针,好吧,听起来也可以实现,但是这种实现相当顽固,如果创建的数组不够大怎么办,而会不会可能创建的数组又太大了。
使用链表才是解决方式,上述做法只适合刚接触编程不久的情况下,使用的临时方案。

下面假设我已经做好了该“定制”的链表数据类型。
那么将它使用在递归实现的深度优先算法中是很简洁的:

#include "utils.h"

#include "tree.h"
#include "list.h"

extern TreeNode_t *p_root;

[...]

int main(int argc, char const *argv[])
{
    setup();

    RespList_t *response_string = NULL;
    response_string = DFS_traversal(p_root, "");

    RespNode_t *curr = NULL;
    curr = response_string->get_head(response_string);
    while(curr){
        printf("%s\n", curr->resp_str);
        curr = curr->next;
    }

    response_string->__del__(response_string);

    return 0;
}

可以看到,使用这个链表其实只有两行,声明类型+赋值,最后析构该链表。

中间这些行:

    RespNode_t *curr = NULL;
    curr = response_string->get_head(response_string);
    while(curr){
        printf("%s\n", curr->resp_str);
        curr = curr->next;
    }

其实是在使用链表内的节点的值的代码,这部分代码其实也可以通过 OOPC 思想(基于对象的编程范式)将其实现在一个函数中。不过因为这段代码只不过是输出到标准输出,所以我略去了这一实现。

那么,深度优先遍历算法的实现变成了如何:

RespList_t* DFS_traversal(
    const TreeNode_t * const p_root,
    const char *parent_path
){
    if (p_root==NULL || parent_path==NULL)
        exit(1);

    RespList_t *resp_list = RespList();

    TreeNode_t *p_curr = p_root;
    printf("↓ func stack push: %-30s↓\n", p_curr->name);
    char __parent_path[256] = {'\0'};
    do {
        if (!strlen(parent_path))
            sprintf(__parent_path, "%s", p_curr->name);
        else
            sprintf(__parent_path, "%s.%s", parent_path, p_curr->name);

        if (p_curr->p_child == NULL) {  // no child node
            RespNode_t *resp_node = (RespNode_t *)malloc(sizeof(RespNode_t));
            if (resp_node == NULL){
                printf("Fatal, malloc fail.\n");
                exit(1);
            }
            resp_node->next = NULL;
            resp_node->resp_str = p_curr->get_response_str(p_curr, __parent_path, GPV);
            resp_list->append(resp_list, resp_node);
        }
        else {
            RespList_t *tmp = DFS_traversal(p_curr->p_child, __parent_path);
            resp_list->extend(resp_list, tmp);
            printf("↑ func stack pop: %-30s↑\n", p_curr->name);
        }
    }while(NULL != (p_curr = p_curr->p_brother));

    return resp_list;
}

如果上面代码删去了一些错误判断,和不需要的辅助输出,那么和原先的代码相差无几
n/a
区别就是相在的代码将“遍历”的到的结构保存在了一个链表中。

这段代码使用了定制的 get value 相关函数,运行得到的输出如下:

↓ func stack push: InternetGatewayDevice         ↓
↓ func stack push: DeviceInfo                    ↓
↓ func stack push: Manufacturer                  ↓
↑ func stack pop: DeviceInfo                    ↑
↓ func stack push: 1                             ↓
↓ func stack push: WLANConfiguration             ↓
↓ func stack push: 1                             ↓
↓ func stack push: Enable                        ↓
↓ func stack push: ErrorsSent                    ↓
↑ func stack pop: Stats                         ↑
↑ func stack pop: 1                             ↑
↑ func stack pop: WLANConfiguration             ↑
↓ func stack push: 1                             ↓
↓ func stack push: Name                          ↓
↑ func stack pop: 1                             ↑
↑ func stack pop: LANEthernetInterfaceConfig    ↑
↑ func stack pop: 1                             ↑
↑ func stack pop: LANDevice                     ↑
↑ func stack pop: InternetGatewayDevice         ↑
<ParameterValueStruct>
	<Name>InternetGatewayDevice.DeviceInfo.Manufacturer</Name>
	<Value xsi:type="xsd:string">Manufacturer</Value>
</ParameterValueStruct>
<ParameterValueStruct>
	<Name>InternetGatewayDevice.DeviceInfo.ManufacturerOUT</Name>
	<Value xsi:type="xsd:string">ManufacturerOUT</Value>
</ParameterValueStruct>
<ParameterValueStruct>
	<Name>InternetGatewayDevice.DeviceInfo.ModelName</Name>
	<Value xsi:type="xsd:string">ModelName</Value>
</ParameterValueStruct>
<ParameterValueStruct>
	<Name>InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.Enable</Name>
	<Value xsi:type="xsd:string">Enable</Value>
</ParameterValueStruct>
<ParameterValueStruct>
	<Name>InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.BSSID</Name>
	<Value xsi:type="xsd:string">BSSID</Value>
</ParameterValueStruct>
<ParameterValueStruct>
	<Name>InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.Stats.ErrorsSent</Name>
	<Value xsi:type="xsd:string">ErrorsSent</Value>
</ParameterValueStruct>
<ParameterValueStruct>
	<Name>InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.Stats.ErrorsReceived</Name>
	<Value xsi:type="xsd:string">ErrorsReceived</Value>
</ParameterValueStruct>
<ParameterValueStruct>
	<Name>InternetGatewayDevice.LANDevice.1.LANEthernetInterfaceConfig.1.Name</Name>
	<Value xsi:type="xsd:string">Name</Value>
</ParameterValueStruct>
<ParameterValueStruct>
	<Name>InternetGatewayDevice.LANDevice.1.LANEthernetInterfaceConfig.1.Enable</Name>
	<Value xsi:type="xsd:string">Enable</Value>
</ParameterValueStruct>

从上面的输出也可以看出,带有箭头的是在 DFS_traversal 函数内的 printf 输出。
而后面的 XML 格式是获取 parametervalue 之后,储存在链表后的统一输出。


值得一提的是 DFS_traversal 函数使用了链表数据类型的两个函数:
.append:

resp_node->resp_str = p_curr->get_response_str(p_curr, __parent_path, GPV);
resp_list->append(resp_list, resp_node);


.extend:

RespList_t *tmp = DFS_traversal(p_curr->p_child, __parent_path);
resp_list->extend(resp_list, tmp);

这两个函数的用法和 Python 语言中 list 类型的 .append.extend 一致。

另外使用了 RespList_t *resp_list = RespList(); 这种写法的构造函数。这构造函数在我看来是全文最亮眼的地方。
另外还有一处析构函数调用这里也值得点出:response_string->__del__(response_string); 这个析构函数管理着 RespList 链表的内存释放(参见附 C 还有一个 RespList 内部辅助的内存释放函数)。

具体的 RespList 链表实现代码请参见 附 C

虽然我将链表的实现放在了文章末尾。但是其实 list.c 中的链表实现代码才是全文的精华。



4 关于 memory leak

使用了 malloc 之后,自然而然地脑子里绷紧了一根弦 – 有没有正确地释放内存,会不会有内存泄漏?

在使用 C 语言编程多年来,这倒是我第一次真正使用 malloc 和 链表,加上一点点遍历算法。倒也没想到第一次实现确实蛮实用的。

结论是关于 memory leak,使用 附C 中的 list.c 中的实现不会有问题,只要正常地调用 __del__ 即可。

对此我测试过 for 循环语句中调用上面用到的 DFT 函数,使用全局 RespList_t * 变量,使用多线程(pthread)等等,并且结合了 valgrind 分析工具运行测试。证明了我可以很负责任地说我的 Resp List 实现不存在内存泄漏的问题。


但是我为什么要强调这一点?
实际上我第一次编辑本文的时候,是没有这一节的。因为我有自信实现上不存在内存泄漏问题。
但是后来在我正在解决的,优化的一个通信设备上的 TR069 XML-RPC 实现上使用了本文的 Resp List,发生了内存泄漏的情况。对此我确实很不解…
于是我对 Resp List 本身的实现,对几个函数都做了一些测试。同时也对使用 Resp List 的不同方式做了测试 - 即上文提到的 for 循环中使用、使用全局量、使用多线程。最后的结论是不可能有内存泄漏。
但是从加入优化后的 solution 的设备运行现象来看,确实又有内存泄漏的情况发生。

这或许是因为设备是“嵌入式 LINUX” 的,有某些我暂不清楚的诡异之处?
所以本着节省读者时间,和我已经花过时间,也做一下记录的原则,在此对使用现象做了上述说明。


对于本节的内容,总结就是:

  1. Resp List 的实现没有问题。在 LINUX 主机上使用和经过充分的测试,不存在内存泄漏的可能性(在正确地使用情况下)。

  2. 但是我在将 Resp List 加入到为了优化运行速度的嵌入式 LINUX 设备上(为了能够使用多线程,同时保证 DFT 算法结构的顺序一致性)确实发生了内存泄漏的情况。具体的原因我还在分析;或许泄漏的原因根本就不是和 Resp List 的实现有关,而是可能将会发现某些搞笑的原因造成的。

    2019/06/19 更新。确实是很搞笑的原因造成的。
    在优化的代码上(该整个程序大约在 20K line 左右),程序原先就是内存泄漏的。
    幸运的是,内存泄漏的位置刚好是我优化的部位附近,所以还算比较顺利地找到了原因(原来编写代码的人在调用数个 json 库的构造 object 函数,使用后没有全部按个释放)。


因为在考虑 C 的内存管理和分析问题,所以最后实现了一份使用二叉树管理内存的方式,这里 ? 是实现原理和过程等细节,有兴趣的读者可以看看。



总结

本文开头介绍了“树”这一数据结构。并且介绍了其 C 语言的实现树数据结构的方式。并且在附 B 中给出了树数据结构的实现、创建节点、构造树 的完整代码。

然后展示了遍历树结构的深度优先算法。

最后使用(基于对象实现的)链表改进了深度优先算法的实现函数。



Reference

n/a



附 A

utils.h 文件内容:

#ifndef __UTILS_H__
#define __UTILS_H__


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>


#endif /* __UTILS_H__ */

Makefile:


all: tree.o list.o
	gcc main.c tree.o list.o -o a.out

tree.o: tree.c tree.h

list.o: list.c list.h

用法:

$ make && ./a.out
...the output...


附 B

tree.c:

#include "tree.h"

char *generic_get_response_str(
	TreeNode_t *me,
	const char *full_path, Action_t action
){
	const char *param_xml_head = "<ParameterValueStruct>";
	const char *path_xml_head = "<Name>";
	const char *path_xml_tail = "</Name>";
	const char *value_xml_head = "<Value xsi:type=\"xsd:string\">";
	const char *value_xml_tail = "</Value>";
	const char *param_xml_tail = "</ParameterValueStruct>";

	char __resp[10240] = {'\0'};
	snprintf(__resp, sizeof(__resp), "%s", param_xml_head);
	strcat(__resp, "\n\t");
	strcat(__resp, path_xml_head);
	strcat(__resp, full_path);
	strcat(__resp, path_xml_tail);
	strcat(__resp, "\n\t");
	strcat(__resp, value_xml_head);
	strcat(__resp, me->name);
	strcat(__resp, value_xml_tail);
	strcat(__resp, "\n");
	strcat(__resp, param_xml_tail);

	char *result_resp = NULL;
	if (NULL == (result_resp = malloc(sizeof(char) * (strlen(__resp) + 1)))){
		printf("malloc fail!\n");
		return NULL;
	}
	memset(result_resp, '\0', (sizeof(char) * (strlen(__resp) + 1)));
	memcpy(result_resp, __resp, strlen(__resp));
	// snprintf(result_resp, (strlen(__resp) + 1), "%s", __resp);

	return result_resp;
}

TreeNode_t igd = {
	"InternetGatewayDevice", NULL, NULL,
	generic_get_response_str
};

TreeNode_t igd_deviceinfo = {
	"DeviceInfo", NULL, NULL,
	generic_get_response_str
};

TreeNode_t igd_deviceinfo_manufacturer = {
	"Manufacturer",	NULL, NULL,
	generic_get_response_str
};

TreeNode_t igd_deviceinfo_manufactureroui = {
	"ManufacturerOUT", NULL, NULL, 
	generic_get_response_str
};
TreeNode_t igd_deviceinfo_modelname = {
	"ModelName", NULL, NULL, 
	generic_get_response_str
};

TreeNode_t igd_landevice = {
	"LANDevice", NULL, NULL, 
	generic_get_response_str
};

TreeNode_t igd_landevice_1 = {
	"1", NULL, NULL, 
	generic_get_response_str
};

TreeNode_t igd_landevice_1_wlanconf = {
	"WLANConfiguration", NULL, NULL, 
	generic_get_response_str
};

TreeNode_t igd_landevice_1_wlanconf_1 = {
	"1", NULL, NULL, 
	generic_get_response_str
};

TreeNode_t igd_landevice_1_wlanconf_1_enable = {
	"Enable", NULL, NULL, 
	generic_get_response_str
};
TreeNode_t igd_landevice_1_wlanconf_1_bssid = {
	"BSSID", NULL, NULL, 
	generic_get_response_str
};

TreeNode_t igd_landevice_1_wlanconf_1_stats = {
	"Stats", NULL, NULL, 
	generic_get_response_str
};

TreeNode_t igd_landevice_1_wlanconf_1_stats_errorss = {
	"ErrorsSent", NULL, NULL, 
	generic_get_response_str
};
TreeNode_t igd_landevice_1_wlanconf_1_stats_errorsr = {
	"ErrorsReceived", NULL, NULL, 
	generic_get_response_str
};

TreeNode_t igd_landevice_1_lanethifconf = {
	"LANEthernetInterfaceConfig", NULL, NULL, 
	generic_get_response_str
};

TreeNode_t igd_landevice_1_lanethifconf_1 = {
	"1", NULL, NULL, generic_get_response_str
};

TreeNode_t igd_landevice_1_lanethifconf_1_name = {
	"Name", NULL, NULL, generic_get_response_str
};
TreeNode_t igd_landevice_1_lanethifconf_1_enable = {
	"Enable", NULL, NULL, generic_get_response_str
};

const TreeNode_t *p_root = &igd;


void build_tr098_model_tree(){
	igd.p_child = &igd_deviceinfo;

	igd_deviceinfo.p_child = &igd_deviceinfo_manufacturer;
	igd_deviceinfo.p_brother = &igd_landevice;

	igd_deviceinfo_manufacturer.p_brother = &igd_deviceinfo_manufactureroui;
	igd_deviceinfo_manufactureroui.p_brother = &igd_deviceinfo_modelname;

	igd_landevice.p_child = &igd_landevice_1;
	igd_landevice_1.p_child = &igd_landevice_1_wlanconf;
	igd_landevice_1_wlanconf.p_child = &igd_landevice_1_wlanconf_1;
	igd_landevice_1_wlanconf.p_brother = &igd_landevice_1_lanethifconf;

	igd_landevice_1_wlanconf_1.p_child = &igd_landevice_1_wlanconf_1_enable;
	igd_landevice_1_wlanconf_1_enable.p_brother = &igd_landevice_1_wlanconf_1_bssid;
	igd_landevice_1_wlanconf_1_bssid.p_brother = &igd_landevice_1_wlanconf_1_stats;

	igd_landevice_1_wlanconf_1_stats.p_child = &igd_landevice_1_wlanconf_1_stats_errorss;
	igd_landevice_1_wlanconf_1_stats_errorss.p_brother = &igd_landevice_1_wlanconf_1_stats_errorsr;

	igd_landevice_1_lanethifconf.p_child = &igd_landevice_1_lanethifconf_1;
	igd_landevice_1_lanethifconf_1.p_child = &igd_landevice_1_lanethifconf_1_name;
	igd_landevice_1_lanethifconf_1_name.p_brother = &igd_landevice_1_lanethifconf_1_enable;

}

tree.h:

#ifndef __TREE_H__
#define __TREE_H__

#include "utils.h"


typedef enum Action{
    GPN=0,
    GPV,
    SPV,
    GPA,
    SPA,
    Invliad
} Action_t;


typedef struct TreeNode_s {
    const char *name;
    struct TreeNode_s *p_brother;
    struct TreeNode_s *p_child;
    char* (*get_response_str)(struct TreeNode_s* me, const char *full_path, Action_t action);
} TreeNode_t;


extern void build_tr098_model_tree();


#endif /* __TREE_H__ */


附 C

list.h:

#ifndef __LIST_H__
#define __LIST_H__

#include "utils.h"


typedef struct RespNode_s {
	char *resp_str;
	struct RespNode_s *next;
} RespNode_t;


typedef struct RespList_s
{
	struct RespNode_s *dummy_head;
	struct RespNode_s *tail_node;

	void (*free)(struct RespList_s* me);
	void (*__del__)(struct RespList_s* me);

	void (*append)(struct RespList_s* me, RespNode_t*);
	void (*extend)(struct RespList_s* me, struct RespList_s*);

	RespNode_t* (*get_head)(struct RespList_s* me);
} RespList_t;


RespList_t *RespList(void);
void respList_free(struct RespList_s* me);
void respList__del__(struct RespList_s* me);
RespNode_t* respList_get_head(struct RespList_s* me);
void respList_append(struct RespList_s* me, RespNode_t* _new);
void respList_extend(struct RespList_s* me, struct RespList_s* src);



#endif /* __LIST_H__ */

list.c:

#include <assert.h>
#include "list.h"

RespList_t *RespList(void){
    RespList_t *result = NULL;
    RespNode_t *dummy_head = NULL;
    if(NULL == (result = malloc(sizeof(RespList_t)))){
        return NULL;
    }
    if (NULL == (dummy_head = malloc(sizeof(RespNode_t)))){
        return NULL;
    }
    result->dummy_head = dummy_head;
    result->dummy_head->resp_str = NULL;  // dummy_head->resp_str always be NULL.
    result->dummy_head->next = NULL;

    result->tail_node = dummy_head;

    result->free = respList_free;
    result->__del__ = respList__del__;
    result->append = respList_append;
    result->extend = respList_extend;
    result->get_head = respList_get_head;

    return result;
}

void respList_free(struct RespList_s* me){
    assert(me->get_head(me) == NULL);
        // printf("Fatal! destructor first befor free\n");
        // exit(1);
    if(me->dummy_head)
        free(me->dummy_head);
        me->dummy_head = NULL;

    if(me)
        free(me);
}

void respList__del__(struct RespList_s* me){
    RespNode_t *tmp = NULL;
    RespNode_t *p_curr_node = me->dummy_head->next;
    me->dummy_head->next = NULL;
    while(p_curr_node != NULL){
        free(p_curr_node->resp_str);
        tmp = p_curr_node;
        p_curr_node = p_curr_node->next;
        free(tmp);
        tmp = NULL;
    }
    me->free(me);
}

RespNode_t* respList_get_head(struct RespList_s* me){
    return me->dummy_head->next;
}

void respList_append(struct RespList_s* me, RespNode_t* _new){
    if (me==NULL){
        printf("me in .append shouldn't be NULL\n");
        exit(1);
    }
    if (_new == NULL)
        return;

    me->tail_node->next = _new;
    me->tail_node = me->tail_node->next;
}

void respList_extend(struct RespList_s* me, struct RespList_s* src){
    if (src == NULL)
        return;
    if (src->get_head(src) == NULL){
        src->free(src);
        return;
    }

    me->tail_node->next = src->get_head(src);
    me->tail_node = me->tail_node->next;
    while(me->tail_node->next){
        me->tail_node = me->tail_node->next;
    }
    src->dummy_head->next = NULL;
    src->free(src);
}



附 D

完整 main.c 文件内容:

#include "utils.h"

#include "tree.h"
#include "list.h"

extern TreeNode_t *p_root;



RespList_t* DFS_traversal(
    const TreeNode_t * const p_root,
    const char *parent_path
){
    if (p_root==NULL || parent_path==NULL)
        exit(1);

    RespList_t *resp_list = RespList();

    TreeNode_t *p_curr = p_root;
    char __parent_path[256] = {'\0'};
    printf("↓ func stack push: %-30s↓\n", p_curr->name);
    do {
        if (!strlen(parent_path))
            sprintf(__parent_path, "%s", p_curr->name);
        else
            sprintf(__parent_path, "%s.%s", parent_path, p_curr->name);

        if (p_curr->p_child == NULL) {  // no child node
            // printf("%s.%s\n", parent_path, p_curr->name);
            RespNode_t *resp_node = (RespNode_t *)malloc(sizeof(RespNode_t));
            if (resp_node == NULL){
                printf("Fatal, malloc fail.\n");
                exit(1);
            }
            resp_node->next = NULL;
            resp_node->resp_str = p_curr->get_response_str(p_curr, __parent_path, GPV);
            resp_list->append(resp_list, resp_node);
        }
        else {
            RespList_t *tmp = DFS_traversal(p_curr->p_child, __parent_path);
            resp_list->extend(resp_list, tmp);
            printf("↑ func stack pop: %-30s↑\n", p_curr->name);
        }
    }while(NULL != (p_curr = p_curr->p_brother));

    return resp_list;
}


void setup(){
    build_tr098_model_tree(); 
}

int main(int argc, char const *argv[])
{
    setup();

    RespList_t *response_string = NULL;
    response_string = DFS_traversal(p_root, "");
    RespNode_t *curr = NULL;
    curr = response_string->get_head(response_string);
    while(curr){
        printf("%s\n", curr->resp_str);
        curr = curr->next;
    }
    response_string->__del__(response_string);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值