Overview
提醒: 这篇博客很长,代码有点儿长,内容有点儿难度
全文要点:
- 树数据结构
- 深度优先算法(遍历)
- 链表
- 内存管理
- 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;
}
如果上面代码删去了一些错误判断,和不需要的辅助输出,那么和原先的代码相差无几
区别就是相在的代码将“遍历”的到的结构保存在了一个链表中。
这段代码使用了定制的 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 格式是获取 parameter
的 value
之后,储存在链表后的统一输出。
值得一提的是 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” 的,有某些我暂不清楚的诡异之处?
所以本着节省读者时间,和我已经花过时间,也做一下记录的原则,在此对使用现象做了上述说明。
对于本节的内容,总结就是:
-
Resp List
的实现没有问题。在 LINUX 主机上使用和经过充分的测试,不存在内存泄漏的可能性(在正确地使用情况下)。 -
但是我在将
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;
}