在Glib中,链表有单向链表和双向链表两种,单向链表只有一个next指针,双向链表有prev和next两个指针。除非对程序的内存使用有非常严格的限制,一般情况下,尽量使用双向链表,这在查找、插入等操作时比单向链表方便很多。
单向链表的数据结构为GSList,双向链表的数据结构为GList,两者操作方式基本相同,只不过单向链表比双向链表少很多操作函数。
单向链表数据结构
struct GSList {
gpointer data;
GSList *next;
};
双向链表数据结构
struct GList {
gpointer data;
GList *next;
GList *prev;
};
GList * g_list_append ()
GList * g_list_prepend ()
GList * g_list_insert ()
GList * g_list_insert_before ()
GList * g_list_insert_sorted ()
GList * g_list_remove ()
GList * g_list_remove_link ()
GList * g_list_delete_link ()
GList * g_list_remove_all ()
void g_list_free ()
void g_list_free_full ()
GList * g_list_alloc ()
void g_list_free_1 ()
guint g_list_length ()
GList * g_list_copy ()
GList * g_list_copy_deep ()
GList * g_list_reverse ()
GList * g_list_sort ()
gint (*GCompareFunc) ()
GList * g_list_insert_sorted_with_data ()
GList * g_list_sort_with_data ()
gint (*GCompareDataFunc) ()
GList * g_list_concat ()
void g_list_foreach ()
void (*GFunc) ()
GList * g_list_first ()
GList * g_list_last ()
#define g_list_previous()
#define g_list_next()
GList * g_list_nth ()
gpointer g_list_nth_data ()
GList * g_list_nth_prev ()
GList * g_list_find ()
GList * g_list_find_custom ()
gint g_list_position ()
gint g_list_index ()
创建
GList
插入
GList * g_list_append ()
GList * g_list_prepend ()
GList * g_list_insert ()
GList * g_list_insert_before ()
逆序
GList * g_list_reverse ()
释放
void g_list_free ()
void g_list_free_full ()
void g_list_free_1 ()
遍历
void g_list_foreach ()
测长
guint g_list_length ()
访问
GList * g_list_first ()
GList * g_list_last ()
#define g_list_previous()
#define g_list_next()
查找
GList * g_list_nth ()
gpointer g_list_nth_data ()
GList * g_list_nth_prev ()
gint g_list_position ()
gint g_list_index ()
GList * g_list_find ()
GList * g_list_find_custom ()
排序
GList * g_list_sort ()
GList * g_list_sort_with_data ()
GList * g_list_insert_sorted ()
GList * g_list_insert_sorted_with_data ()
删除
GList * g_list_remove ()
GList * g_list_remove_all ()
GList * g_list_remove_link ()
GList * g_list_delete_link ()
拼接
GList * g_list_concat ()
拷贝
GList * g_list_copy ()
GList * g_list_copy_deep ()
GList没有专门创建链表的函数,只需要定义一个节点,即创建了链表。
GList *list;
节点的插入有四个基本函数
GList * g_list_append ()
GList * g_list_prepend ()
GList * g_list_insert ()
GList * g_list_insert_before ()
void g_list_free () //释放所有节点
void g_list_free_full () //释放所有节点,并指定私有数据的释放方式
void g_list_free_1 () //释放一个节点,使用较少
下面来看一个节点插入及释放的例子。
源码见glib_examples\glib_list\glib_list_append_free
#include
gint main(gint argc, gchar **argv)
{
GList *list = NULL;
GList *iter= NULL;
list = g_list_append(list, GINT_TO_POINTER(1));
list = g_list_append(list, GINT_TO_POINTER(3));
list = g_list_append(list, GINT_TO_POINTER(5));
for (iter = list; iter != NULL; iter = iter->next)
{
g_print("data is: %d \n", GPOINTER_TO_INT(iter->data));
}
g_list_free(list);
return 0;
}
运行结果
data is: 1
data is: 3
data is: 5
分析:
首先创建一个空链表list,并插入三个整数,然后把链表的值全部打印出来,最后释放整个链表。使用valgrind工具检查一下是否有内存泄露。命令如下
valgrind --tool=memcheck --leak-check=full ./glib_list_append_free
[root@centos7_6 build]# valgrind --tool=memcheck --leak-check=full ./glib_list_append_free
==26247== Memcheck, a memory error detector
==26247== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==26247== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==26247== Command: ./glib_list_append_free
==26247==
data is: 1
data is: 3
data is: 5
==26247==
==26247== HEAP SUMMARY:
==26247== in use at exit: 2,214 bytes in 9 blocks
==26247== total heap usage: 32 allocs, 23 frees, 102,209 bytes allocated
==26247==
==26247== LEAK SUMMARY:
==26247== definitely lost: 0 bytes in 0 blocks
==26247== indirectly lost: 0 bytes in 0 blocks
==26247== possibly lost: 0 bytes in 0 blocks
==26247== still reachable: 2,214 bytes in 9 blocks
==26247== suppressed: 0 bytes in 0 blocks
==26247== Reachable blocks (those to which a pointer was found) are not shown.
==26247== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==26247==
==26247== For counts of detected and suppressed errors, rerun with: -v
==26247== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 f
可以看到,没有内存泄露。
上面介绍了一种链表的遍历方法,实际上,链表还有一个函数来专门进行元素遍历。
void
g_list_foreach (GList *list,
GFunc func,
gpointer user_data);
这个函数的第二个参数是一个函数指针,也就是常说的回调函数,用来指定如何遍历节点,这在GLib中是一种非常常见的用法,后面会经常见到。
第二个参数的指针函数原型如下:
void
(*GFunc) (gpointer data,
gpointer user_data);
举例:可以实现如下回调函数,将结构体成员打印出来。
void _list_interger_print(gpointer data, gpointer user_data)
{
g_print("data is: %d, user_data is: %s \n", GPOINTER_TO_INT(data), (gchar *)user_data);
}
这个回调函数的第一个参数data即是GList链表的data成员,第二个参数user_data是g_list_foreach的第三个参数,为回调函数的私有参数。下面程序演示使用g_list_foreach对链表进行遍历。
源码见glib_examples\glib_list\glib_list_foreach
#include
void _list_interger_print(gpointer data, gpointer user_data)
{
g_print("data is: %d, user_data is: %s \n", GPOINTER_TO_INT(data), (gchar *)user_data);
}
gint main(gint argc, gchar **argv)
{
GList *list = NULL;
// GList *iter= NULL;
list = g_list_append(list, GINT_TO_POINTER(1));
list = g_list_append(list, GINT_TO_POINTER(3));
list = g_list_append(list, GINT_TO_POINTER(5));
// for (iter = list; iter != NULL; iter = iter->next)
// {
// g_print("data is: %d \n", GPOINTER_TO_INT(iter->data));
// }
g_list_foreach(list, _list_interger_print, (gchar *)"" );
g_list_free(list);
return 0;
}
运行结果如下:
data is: 1, user_data is: <user data>
data is: 3, user_data is: <user data>
data is: 5, user_data is: <user data>
GLib为节点插入提供了四个函数:
GList * g_list_append ()
GList * g_list_prepend ()
GList * g_list_insert ()
GList * g_list_insert_before ()
第一个g_list_append前面已经介绍过了,此函数在大量插入节点时效率较低,原因是GList数据结构没有存储尾节点,因此在尾插时,只能先从头开始遍历,找到尾节点,再插入。在官方文档中介绍了一种常用的节点插入方法,即头插再逆序的方法,使用g_list_prepend插入所有节点,再使用g_list_reverse逆序。g_list_prepend的使用方法非常简单,与g_list_append几乎一模一样。
源码见glib_examples\glib_list\glib_list_prepend
#include
void _list_interger_print(gpointer data, gpointer user_data)
{
g_print("data is: %d, user_data is: %s \n", GPOINTER_TO_INT(data), (gchar *)user_data);
}
gint main(gint argc, gchar **argv)
{
GList *list = NULL;
list = g_list_prepend(list, GINT_TO_POINTER(1));
list = g_list_prepend(list, GINT_TO_POINTER(3));
list = g_list_prepend(list, GINT_TO_POINTER(5));
g_list_foreach(list, _list_interger_print, (gchar *)"" );
g_list_free(list);
return 0;
}
运行结果:
data is: 5, user_data is: <user data>
data is: 3, user_data is: <user data>
data is: 1, user_data is: <user data>
对上述链表进行逆序。
源码见glib_examples\glib_list\glib_list_prepend_reverse
#include
void _list_interger_print(gpointer data, gpointer user_data)
{
g_print("data is: %d, user_data is: %s \n", GPOINTER_TO_INT(data), (gchar *)user_data);
}
gint main(gint argc, gchar **argv)
{
GList *list = NULL;
list = g_list_prepend(list, GINT_TO_POINTER(1));
list = g_list_prepend(list, GINT_TO_POINTER(3));
list = g_list_prepend(list, GINT_TO_POINTER(5));
list = g_list_reverse(list);
g_list_foreach(list, _list_interger_print, (gchar *)"" );
g_list_free(list);
return 0;
}
可以看到,头插再逆序的方法与尾插法结果相同。
data is: 1, user_data is: <user data>
data is: 3, user_data is: <user data>
data is: 5, user_data is: <user data>
接下来来看一下g_list_insert函数,其原型如下:
GList *
g_list_insert (GList *list,
gpointer data,
gint position);
第二个参数是要插入的数据,这个数据不需要包含prev和next指针的,链表自身会自动将节点补充完整。
第三个参数是插入的位置,从0开始,如果该值小于0或大于整个链表长度,则节点会被插入到链表尾。
链表插入举例:
源码见glib_examples\glib_list\glib_list_insert
#include
void _list_interger_print(gpointer data, gpointer user_data)
{
g_print("data is: %d, user_data is: %s \n", GPOINTER_TO_INT(data), (gchar *)user_data);
}
gint main(gint argc, gchar **argv)
{
GList *list = NULL;
list = g_list_append(list, GINT_TO_POINTER(1));
list = g_list_append(list, GINT_TO_POINTER(3));
list = g_list_append(list, GINT_TO_POINTER(5));
list = g_list_insert(list, GINT_TO_POINTER(2), 1);
g_list_foreach(list, _list_interger_print, (gchar *)"" );
g_list_free(list);
return 0;
}
运行结果:
data is: 1, user_data is: <user data>
data is: 2, user_data is: <user data>
data is: 3, user_data is: <user data>
data is: 5, user_data is: <user data>
下面介绍另一种插入方法:g_list_insert_before函数和g_list_nth函数的组合。
GList *
g_list_insert_before (GList *list,
GList *sibling,
gpointer data);
第二个参数表示要在此节点之前插入data数据,很明显,需要先得到这个节点。
链表的访问和查找函数都可以得到一个具体的节点,g_list_nth可以根据下标得到链表中的某一节点。
GList *
g_list_nth (GList *list,
guint n);
下面举例演示g_list_insert_before函数和g_list_nth函数的用法。
源码见glib_examples\glib_list\glib_list_insert_before
#include
void _list_interger_print(gpointer data, gpointer user_data)
{
g_print("data is: %d, user_data is: %s \n", GPOINTER_TO_INT(data), (gchar *)user_data);
}
gint main(gint argc, gchar **argv)
{
GList *list = NULL;
GList *l = NULL;
list = g_list_append(list, GINT_TO_POINTER(1));
list = g_list_append(list, GINT_TO_POINTER(3));
list = g_list_append(list, GINT_TO_POINTER(5));
list = g_list_insert(list, GINT_TO_POINTER(2), 1);
g_list_foreach(list, _list_interger_print, (gchar *)"" );
l = g_list_nth(list, 3);
list = g_list_insert_before(list, l, GINT_TO_POINTER(4));
g_list_foreach(list, _list_interger_print, (gchar *)"" );
g_list_free(list);
return 0;
}
运行结果:
data is: 1, user_data is: <before g_list_insert_before>
data is: 2, user_data is: <before g_list_insert_before>
data is: 3, user_data is: <before g_list_insert_before>
data is: 5, user_data is: <before g_list_insert_before>
data is: 1, user_data is: <after g_list_insert_before>
data is: 2, user_data is: <after g_list_insert_before>
data is: 3, user_data is: <after g_list_insert_before>
data is: 4, user_data is: <after g_list_insert_before>
data is: 5, user_data is: <after g_list_insert_before>
链表有以下四个节点访问函数
GList * g_list_first ()
GList * g_list_last ()
#define g_list_previous()
#define g_list_next()
举例如下:
源码见glib_examples\glib_list\glib_list_access
#include
gint main(gint argc, gchar **argv)
{
GList *list = NULL;
GList *l = NULL;
#if 1
list = g_list_append(list, GINT_TO_POINTER(1));
list = g_list_append(list, GINT_TO_POINTER(3));
list = g_list_append(list, GINT_TO_POINTER(5));
list = g_list_append(list, GINT_TO_POINTER(7));
#endif
l = g_list_last(list);
g_print("last element: %d \n", (NULL == l) ? 99999 : GPOINTER_TO_INT(l->data));
l = g_list_previous(l);
g_print("last->prev element: %d \n", (NULL == l) ? 99999 : GPOINTER_TO_INT(l->data));
l = g_list_first(list);
g_print("first element: %d \n", (NULL == l) ? 99999 : GPOINTER_TO_INT(l->data));
l = g_list_next(l);
g_print("first->next element: %d \n", (NULL == l) ? 99999 : GPOINTER_TO_INT(l->data));
g_list_free(list);
return 0;
}
运行结果:
last element: 7
last->prev element: 5
first element: 1
first->next element: 3
GList * g_list_nth ()
gpointer g_list_nth_data ()
GList * g_list_nth_prev ()
gint g_list_position ()
gint g_list_index ()
GList * g_list_find ()
GList * g_list_find_custom ()
//g_list_nth是查找里面最简单的一个函数,前面已经介绍过,传入下标,得到节点,注意下标以0开始。
GList *
g_list_nth (GList *list,
guint n);
//g_list_nth_data与g_list_nth相仿,只不过g_list_nth返回节点,g_list_nth_data返回节点的值。
gpointer
g_list_nth_data (GList *list,
guint n);
//注意此函数:不是返回第n个节点的前一个节点,而是返回链表中某一节点的向前第n个节点,n为函数的第二个参数。
GList *
g_list_nth_prev (GList *list,
guint n);
//根据链表中的一个节点找到其在链表中的位置
gint
g_list_position (GList *list,
GList *llink);
//根据值找到节点在链表中的位置
gint
g_list_index (GList *list,
gconstpointer data);
//根据节点的值找到节点。
GList *
g_list_find (GList *list,
gconstpointer data);
//根据节点的值找到节点,但比较函数由用户自己实现。
//链表中的每一个节点,都会通过比较函数进行比较,若比较函数返回0,则代表节点已找到,//g_list_find_custom退出。
//本函数我们放到最后再讲。
GList *
g_list_find_custom (GList *list,
gconstpointer data,
GCompareFunc func);
下面是对查找函数的举例演示:
源码见glib_examples\glib_list\glib_list_find
#include
gint main(gint argc, gchar **argv)
{
GList *list = NULL;
GList *l = NULL;
gint n = 0;
list = g_list_append(list, GINT_TO_POINTER(1));
list = g_list_append(list, GINT_TO_POINTER(3));
list = g_list_append(list, GINT_TO_POINTER(5));
list = g_list_append(list, GINT_TO_POINTER(7));
l = g_list_nth(list, 1);
g_print("nth(list,1) element: %d \n", (NULL == l) ? 99999 : GPOINTER_TO_INT(l->data));
n = GPOINTER_TO_INT(g_list_nth_data(list, 1));
g_print("nth_data(list,1) element: %d \n", n);
l = g_list_last(list);
//7->5->3->1
l = g_list_nth_prev(l, 3);
g_print("nth_prev(last,3) element: %d \n", (NULL == l) ? 99999 : GPOINTER_TO_INT(l->data));
l = g_list_last(list);
n = g_list_position(list, l);
g_print("last position: %d \n", n);
n = g_list_index(list, GINT_TO_POINTER(5));
g_print("element 5 position: %d \n", n);
l = g_list_find(list, GINT_TO_POINTER(5));
g_print("element 5 data: %d \n", (NULL == l) ? 99999 : GPOINTER_TO_INT(l->data));
g_list_free(list);
return 0;
}
运行结果:
nth(list,1) element: 3
nth_data(list,1) element: 3
nth_prev(last,2) element: 3
last position: 3
element 5 position: 2
element 5 data: 5
可以看到,在GList中,节点以0为下标,即链表的第一个节点的下标为0。
GList * g_list_sort ()
GList * g_list_sort_with_data ()
GList * g_list_insert_sorted ()
GList * g_list_insert_sorted_with_data ()
//链表排序,排序函数用户自己指定。
GList *
g_list_sort (GList *list,
GCompareFunc compare_func);
//排序函数原型:
gint
(*GCompareFunc) (gconstpointer a,
gconstpointer b);
//排序函数返回值:negative value if a < b ; zero if a = b ; positive value if a > b;ab返回正值
//与g_list_sort功能相似,不过可以向比较函数传入用户数据。
GList *
g_list_sort_with_data (GList *list,
GCompareDataFunc compare_func,
gpointer user_data);
//插入一个数据data,其插入位置由比较函数决定。
//其实现方式为,先通过比较函数找到要插入的链表位置,再将数据插入链表。
//此处要求链表需要是有序的,如果原链表无序,此函数并不能实现插入数据后使链表变为有序状态的功能。
GList *
g_list_insert_sorted (GList *list,
gpointer data,
GCompareFunc func);
//与g_list_insert_sorted功能相似,不过可以向比较函数传入用户数据。
GList *
g_list_insert_sorted_with_data (GList *list,
gpointer data,
GCompareDataFunc func,
gpointer user_data);
下面是针对排序函数的演示程序。
源码见`glib_examples\glib_list\glib_list_sort
#include
void _list_interger_print_func(gpointer data, gpointer user_data)
{
g_print("data is: %d, user_data is: %s \n", GPOINTER_TO_INT(data), (gchar *)user_data);
}
gint _list_interger_cmp_func(gconstpointer a, gconstpointer b)
{
return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
}
gint main(gint argc, gchar **argv)
{
GList *list = NULL;
list = g_list_append(list, GINT_TO_POINTER(1));
list = g_list_append(list, GINT_TO_POINTER(5));
list = g_list_append(list, GINT_TO_POINTER(3));
list = g_list_append(list, GINT_TO_POINTER(7));
g_list_foreach(list, _list_interger_print_func, (gchar *)"before insert_sorted");
list = g_list_insert_sorted(list, GINT_TO_POINTER(4), _list_interger_cmp_func);
g_list_foreach(list, _list_interger_print_func, (gchar *)"after insert_sorted");
list = g_list_sort(list, _list_interger_cmp_func);
g_list_foreach(list, _list_interger_print_func, (gchar *)"after sorted");
g_list_free(list);
return 0;
}
运行结果:
data is: 1, user_data is: before insert_sorted
data is: 5, user_data is: before insert_sorted
data is: 3, user_data is: before insert_sorted
data is: 7, user_data is: before insert_sorted
data is: 1, user_data is: after insert_sorted
data is: 4, user_data is: after insert_sorted
data is: 5, user_data is: after insert_sorted
data is: 3, user_data is: after insert_sorted
data is: 7, user_data is: after insert_sorted
data is: 1, user_data is: after sorted
data is: 3, user_data is: after sorted
data is: 4, user_data is: after sorted
data is: 5, user_data is: after sorted
data is: 7, user_data is: after sorted
先调用g_list_insert_sorted,再调用g_list_sort,可以看出,当链表本身无序时,g_list_insert_sorted仅能在找到比自己大的数据后插入到其前面,并不能排序,而g_list_sort函数可以对链表进行排序。
GList * g_list_remove ()
GList * g_list_remove_all ()
GList * g_list_remove_link ()
GList * g_list_delete_link ()
//删除链表中的节点,如果有多个相同的节点,则仅第一个被删除
GList *
g_list_remove (GList *list,
gconstpointer data);
//删除链表中的节点,如果有多个相同的节点,则所有节点都会被删除
GList *
g_list_remove_all (GList *list,
gconstpointer data);
//将节点从链表移出,但并不释放该节点,返回的是一个自包含的链表,该链表的prev和next指针均为空。调用完本函数后,用户需要自己释放该节点,否则会造成内存泄露。
GList *
g_list_remove_link (GList *list,
GList *llink);
//删除节点,并且节点会被释放
GList *
g_list_delete_link (GList *list,
GList *link_);
节点删除功能演示:
源码见glib_examples\glib_list\glib_list_remove
#include
void _list_interger_print_func(gpointer data, gpointer user_data)
{
g_print("data is: %d, user_data is: %s \n", GPOINTER_TO_INT(data), (gchar *)user_data);
}
gint main(gint argc, gchar **argv)
{
GList *list = NULL;
GList *l = NULL;
list = g_list_append(list, GINT_TO_POINTER(1));
list = g_list_append(list, GINT_TO_POINTER(5));
list = g_list_append(list, GINT_TO_POINTER(3));
list = g_list_append(list, GINT_TO_POINTER(5));
list = g_list_append(list, GINT_TO_POINTER(5));
list = g_list_append(list, GINT_TO_POINTER(7));
g_list_foreach(list, _list_interger_print_func, (gchar *)"original list");
list = g_list_remove(list, GINT_TO_POINTER(5));
g_list_foreach(list, _list_interger_print_func, (gchar *)"after remove 5");
list = g_list_remove_all(list, GINT_TO_POINTER(5));
g_list_foreach(list, _list_interger_print_func, (gchar *)"after remove_all 5");
l = g_list_last(list);
list = g_list_remove_link(list, l);
g_list_free(l);
l = g_list_first(list);
list = g_list_delete_link(list, l);
g_list_foreach(list, _list_interger_print_func, (gchar *)"after remove first and last");
g_list_free(list);
return 0;
}
运行结果:
data is: 1, user_data is: original list
data is: 5, user_data is: original list
data is: 3, user_data is: original list
data is: 5, user_data is: original list
data is: 5, user_data is: original list
data is: 7, user_data is: original list
data is: 1, user_data is: after remove 5
data is: 3, user_data is: after remove 5
data is: 5, user_data is: after remove 5
data is: 5, user_data is: after remove 5
data is: 7, user_data is: after remove 5
data is: 1, user_data is: after remove_all 5
data is: 3, user_data is: after remove_all 5
data is: 7, user_data is: after remove_all 5
data is: 3, user_data is: after remove first and last
屏蔽掉g_list_free(l);一行,使用valgrind工具,可以看到,如果不对g_list_remove_link移出的节点进行释放,会导致内存泄露。
valgrind --tool=memcheck --leak-check=full ./glib_list_remove
[root@centos7_6 build]# valgrind --tool=memcheck --leak-check=full ./glib_list_remove
==27844== Memcheck, a memory error detector
==27844== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==27844== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==27844== Command: ./glib_list_remove
==27844==
data is: 1, user_data is: original list
data is: 5, user_data is: original list
data is: 3, user_data is: original list
data is: 5, user_data is: original list
data is: 5, user_data is: original list
data is: 7, user_data is: original list
data is: 1, user_data is: after remove 5
data is: 3, user_data is: after remove 5
data is: 5, user_data is: after remove 5
data is: 5, user_data is: after remove 5
data is: 7, user_data is: after remove 5
data is: 1, user_data is: after remove_all 5
data is: 3, user_data is: after remove_all 5
data is: 7, user_data is: after remove_all 5
data is: 3, user_data is: after remove first and last
==27844==
==27844== HEAP SUMMARY:
==27844== in use at exit: 2,238 bytes in 10 blocks
==27844== total heap usage: 107 allocs, 97 frees, 500,479 bytes allocated
==27844==
==27844== 24 bytes in 1 blocks are definitely lost in loss record 6 of 10
==27844== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==27844== by 0x4E856D0: g_malloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==27844== by 0x4E9B2ED: g_slice_alloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==27844== by 0x4E7C503: g_list_append (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==27844== by 0x400B80: main (glib_list.c:18)
==27844==
==27844== LEAK SUMMARY:
==27844== definitely lost: 24 bytes in 1 blocks
==27844== indirectly lost: 0 bytes in 0 blocks
==27844== possibly lost: 0 bytes in 0 blocks
==27844== still reachable: 2,214 bytes in 9 blocks
==27844== suppressed: 0 bytes in 0 blocks
==27844== Reachable blocks (those to which a pointer was found) are not shown.
==27844== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==27844==
==27844== For counts of detected and suppressed errors, rerun with: -v
==27844== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
GList * g_list_concat ()
拼接后,不可再释放第二个链表,举例:
源码见glib_examples\glib_list\glib_list_concat
#include
void _list_interger_print_func(gpointer data, gpointer user_data)
{
g_print("data is: %d, user_data is: %s \n", GPOINTER_TO_INT(data), (gchar *)user_data);
}
gint main(gint argc, gchar **argv)
{
GList *list1 = NULL;
GList *list2 = NULL;
list1 = g_list_append(list1, GINT_TO_POINTER(1));
list1 = g_list_append(list1, GINT_TO_POINTER(5));
list2 = g_list_append(list2, GINT_TO_POINTER(3));
list2 = g_list_append(list2, GINT_TO_POINTER(7));
g_list_foreach(list1, _list_interger_print_func, (gchar *)"list1");
g_list_foreach(list2, _list_interger_print_func, (gchar *)"list2");
list1 = g_list_concat(list1, list2);
g_list_foreach(list1, _list_interger_print_func, (gchar *)"list1-concat");
g_list_foreach(list2, _list_interger_print_func, (gchar *)"list2-concat");
g_list_free(list1);
// g_list_free(list2); //should not free list2
return 0;
}
使用g_list_concat移动链表中的元素。
源码见glib_examples\glib_list\glib_list_move
#include
void _list_interger_print_func(gpointer data, gpointer user_data)
{
g_print("data is: %d, user_data is: %s \n", GPOINTER_TO_INT(data), (gchar *)user_data);
}
gint main(gint argc, gchar **argv)
{
GList *list1 = NULL;
GList *list2 = NULL;
GList *l = NULL;
list1 = g_list_append(list1, GINT_TO_POINTER(1));
list1 = g_list_append(list1, GINT_TO_POINTER(5));
list2 = g_list_append(list2, GINT_TO_POINTER(3));
list2 = g_list_append(list2, GINT_TO_POINTER(7));
g_list_foreach(list1, _list_interger_print_func, (gchar *)"list1");
g_list_foreach(list2, _list_interger_print_func, (gchar *)"list2");
g_print("===test concat=== \n");
list1 = g_list_concat(list1, list2);
g_list_foreach(list1, _list_interger_print_func, (gchar *)"list1-concat");
g_list_foreach(list2, _list_interger_print_func, (gchar *)"list2-concat");
g_print("===test move last to front=== \n");
l = g_list_last(list1);
list1 = g_list_remove_link(list1, l);
list1 = g_list_concat(l, list1);
g_list_foreach(list1, _list_interger_print_func, (gchar *)"list1-move-to-front");
g_list_foreach(list2, _list_interger_print_func, (gchar *)"list2-move-to-front");
g_list_free(list1);
return 0;
}
运行结果:
data is: 1, user_data is: list1
data is: 5, user_data is: list1
data is: 3, user_data is: list2
data is: 7, user_data is: list2
===test concat===
data is: 1, user_data is: list1-concat
data is: 5, user_data is: list1-concat
data is: 3, user_data is: list1-concat
data is: 7, user_data is: list1-concat
data is: 3, user_data is: list2-concat
data is: 7, user_data is: list2-concat
===test move last to front===
data is: 7, user_data is: list1-move-to-front
data is: 1, user_data is: list1-move-to-front
data is: 5, user_data is: list1-move-to-front
data is: 3, user_data is: list1-move-to-front
data is: 3, user_data is: list2-move-to-front
GList * g_list_copy ()
GList * g_list_copy_deep ()
举例
源码见glib_examples\glib_list\glib_list_deep_copy_err
#include
void _list_str_print_func(gpointer data, gpointer user_data)
{
g_print("user_data is: %s, data is: %s \n", (gchar *)user_data, (gchar*)data);
}
static gpointer _list_str_copy_deep (gconstpointer value, gpointer data)
{
return (gpointer)g_strdup((const gchar *)value);
}
gint main(gint argc, gchar **argv)
{
GList *list = NULL;
GList *list2 = NULL;
GList *list3 = NULL;
gchar str1[32] = {0};
gchar str2[32] = {0};
g_strlcpy(str1, "hello", sizeof("hello"));
g_strlcpy(str2, "world", sizeof("world"));
list = g_list_append(list, (gpointer)str1);
list = g_list_append(list, (gpointer)str2);
g_print("===test copy===\n");
list2 = g_list_copy(list);
g_list_foreach(list, _list_str_print_func, (gchar *)"list");
g_list_foreach(list2, _list_str_print_func, (gchar *)"list2");
g_strlcpy(str2, "glib", sizeof("glib"));
g_list_foreach(list, _list_str_print_func, (gchar *)"list-change");
g_list_foreach(list2, _list_str_print_func, (gchar *)"list2-change");
g_print("===test deep copy===\n");
list3 = g_list_copy_deep(list, _list_str_copy_deep, NULL);
g_list_foreach(list, _list_str_print_func, (gchar *)"list");
g_list_foreach(list2, _list_str_print_func, (gchar *)"list2");
g_list_foreach(list3, _list_str_print_func, (gchar *)"list3");
g_strlcpy(str1, "welcome", sizeof("welcome"));
g_list_foreach(list, _list_str_print_func, (gchar *)"list-deep-copy");
g_list_foreach(list2, _list_str_print_func, (gchar *)"list2-deep-copy");
g_list_foreach(list3, _list_str_print_func, (gchar *)"list3-deep-copy");
g_list_free(list);
g_list_free(list2);
g_list_free(list3);
return 0;
}
运行结果:
===test copy===
user_data is: list, data is: hello
user_data is: list, data is: world
user_data is: list2, data is: hello
user_data is: list2, data is: world
user_data is: list-change, data is: hello
user_data is: list-change, data is: glib
user_data is: list2-change, data is: hello
user_data is: list2-change, data is: glib
===test deep copy===
user_data is: list, data is: hello
user_data is: list, data is: glib
user_data is: list2, data is: hello
user_data is: list2, data is: glib
user_data is: list3, data is: hello
user_data is: list3, data is: glib
user_data is: list-deep-copy, data is: welcome
user_data is: list-deep-copy, data is: glib
user_data is: list2-deep-copy, data is: welcome
user_data is: list2-deep-copy, data is: glib
user_data is: list3-deep-copy, data is: hello
user_data is: list3-deep-copy, data is: glib
运行结果表明,g_list_copy类似于linux文件系统的符号链接,当链表内容有变化时,符号链接所指向的值也会变化。而g_list_copy_deep则不会受原链表内容变化的影响,因为g_list_copy_deep是对原链表的数值内存进行了拷贝。
上述代码有一处问题。
使用g_list_copy_deep得到了新链表,但未对新链表中使用g_strdup拷贝的节点主动释放,造成了内存泄露。使用valgrind进行检测,可以直观地看到内存泄露的地方。
[root@centos7_6 build]# valgrind --tool=memcheck --leak-check=full ./glib_list_deep_copy_err
==28540== Memcheck, a memory error detector
==28540== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==28540== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==28540== Command: ./glib_list_deep_copy_err
==28540==
===test copy===
user_data is: list, data is: hello
user_data is: list, data is: world
user_data is: list2, data is: hello
user_data is: list2, data is: world
user_data is: list-change, data is: hello
user_data is: list-change, data is: glib
user_data is: list2-change, data is: hello
user_data is: list2-change, data is: glib
===test deep copy===
user_data is: list, data is: hello
user_data is: list, data is: glib
user_data is: list2, data is: hello
user_data is: list2, data is: glib
user_data is: list3, data is: hello
user_data is: list3, data is: glib
user_data is: list-deep-copy, data is: welcome
user_data is: list-deep-copy, data is: glib
user_data is: list2-deep-copy, data is: welcome
user_data is: list2-deep-copy, data is: glib
user_data is: list3-deep-copy, data is: hello
user_data is: list3-deep-copy, data is: glib
==28540==
==28540== HEAP SUMMARY:
==28540== in use at exit: 2,225 bytes in 11 blocks
==28540== total heap usage: 151 allocs, 140 frees, 732,465 bytes allocated
==28540==
==28540== 5 bytes in 1 blocks are definitely lost in loss record 3 of 11
==28540== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28540== by 0x4E856D0: g_malloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==28540== by 0x4E9CBCE: g_strdup (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==28540== by 0x400AB4: _list_str_copy_deep (glib_list_deep_copy_err.c:10)
==28540== by 0x4E7C259: g_list_copy_deep (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==28540== by 0x400C38: main (glib_list_deep_copy_err.c:42)
==28540==
==28540== 6 bytes in 1 blocks are definitely lost in loss record 4 of 11
==28540== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28540== by 0x4E856D0: g_malloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==28540== by 0x4E9CBCE: g_strdup (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==28540== by 0x400AB4: _list_str_copy_deep (glib_list_deep_copy_err.c:10)
==28540== by 0x4E7C22E: g_list_copy_deep (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==28540== by 0x400C38: main (glib_list_deep_copy_err.c:42)
==28540==
==28540== LEAK SUMMARY:
==28540== definitely lost: 11 bytes in 2 blocks
==28540== indirectly lost: 0 bytes in 0 blocks
==28540== possibly lost: 0 bytes in 0 blocks
==28540== still reachable: 2,214 bytes in 9 blocks
==28540== suppressed: 0 bytes in 0 blocks
==28540== Reachable blocks (those to which a pointer was found) are not shown.
==28540== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==28540==
==28540== For counts of detected and suppressed errors, rerun with: -v
==28540== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
需要对在深拷贝时申请的内存进行释放。此处用到了链表释放函数g_list_free_full,本函数原型如下:
void
g_list_free_full (GList *list,
GDestroyNotify free_func);
本函数需要传入一个回调函数,提供每一个节点的释放方法。本函数对链表进行遍历,并根据提供的节点自定义释放方法对每一个节点进行释放。
源码见glib_examples\glib_list\glib_list_deep_copy
#include
static void _list_str_print_func(gpointer data, gpointer user_data)
{
g_print("user_data is: %s, data is: %s \n", (gchar *)user_data, (gchar*)data);
}
static gpointer _list_str_copy_deep (gconstpointer value, gpointer data)
{
return (gpointer)g_strdup((const gchar *)value);
}
static void _list_str_copy_deep_free_func(gpointer data)
{
g_free(data);
}
gint main(gint argc, gchar **argv)
{
GList *list = NULL;
GList *list2 = NULL;
GList *list3 = NULL;
gchar str1[32] = {0};
gchar str2[32] = {0};
g_strlcpy(str1, "hello", sizeof("hello"));
g_strlcpy(str2, "world", sizeof("world"));
list = g_list_append(list, (gpointer)str1);
list = g_list_append(list, (gpointer)str2);
g_print("===test copy===\n");
list2 = g_list_copy(list);
g_list_foreach(list, _list_str_print_func, (gchar *)"list");
g_list_foreach(list2, _list_str_print_func, (gchar *)"list2");
g_strlcpy(str2, "glib", sizeof("glib"));
g_list_foreach(list, _list_str_print_func, (gchar *)"list-change");
g_list_foreach(list2, _list_str_print_func, (gchar *)"list2-change");
g_print("===test deep copy===\n");
list3 = g_list_copy_deep(list, _list_str_copy_deep, NULL);
g_list_foreach(list, _list_str_print_func, (gchar *)"list");
g_list_foreach(list2, _list_str_print_func, (gchar *)"list2");
g_list_foreach(list3, _list_str_print_func, (gchar *)"list3");
g_strlcpy(str1, "welcome", sizeof("welcome"));
g_list_foreach(list, _list_str_print_func, (gchar *)"list-deep-copy");
g_list_foreach(list2, _list_str_print_func, (gchar *)"list2-deep-copy");
g_list_foreach(list3, _list_str_print_func, (gchar *)"list3-deep-copy");
g_list_free(list);
g_list_free(list2);
g_list_free_full(list3, _list_str_copy_deep_free_func);
return 0;
}
再次使用valgrind检测,无内存泄露。
通过上面这个例子,可以得出结论:
GList只会开辟GList结构体的内存,即两个指针和一个指向实际数据的指针,不会为实际数据开辟内存,因此,如果想长期保存实际数据,则不可使用栈内存。如本例所示,若链表为全局变量,而str1,str2为局部变量,则函数运行结束时,str1和str2因为是局部变量而被释放,全局链表的值也变得未知。
链表浅拷贝要注意内存重复释放问题,见下面的例子:
源码见glib_examples\glib_list\glib_list_copy_err
#include
static void _list_str_print_func(gpointer data, gpointer user_data)
{
g_print("user_data is: %s, data is: %s \n", (gchar *)user_data, (gchar*)data);
}
static gpointer _list_str_copy_deep (gconstpointer value, gpointer data)
{
return (gpointer)g_strdup((const gchar *)value);
}
static void _list_str_free_func(gpointer data)
{
g_free((gchar *)data);
}
gint main(gint argc, gchar **argv)
{
GList *list = NULL;
GList *list2 = NULL;
GList *list3 = NULL;
gchar *str1 = NULL;
gchar *str2 = NULL;
str1 = g_strdup("hello");
str2 = g_strdup("world");
list = g_list_append(list, (gpointer)str1);
list = g_list_append(list, (gpointer)str2);
g_print("===test copy===\n");
list2 = g_list_copy(list);
g_list_foreach(list, _list_str_print_func, (gchar *)"list");
g_list_foreach(list2, _list_str_print_func, (gchar *)"list2");
g_print("===test deep copy===\n");
list3 = g_list_copy_deep(list, _list_str_copy_deep, NULL);
g_list_foreach(list, _list_str_print_func, (gchar *)"list");
g_list_foreach(list2, _list_str_print_func, (gchar *)"list2");
g_list_foreach(list3, _list_str_print_func, (gchar *)"list3");
g_list_free_full(list, _list_str_free_func);
g_list_free(list2); // #ERROR!!!: g_list_free_full(list2, _list_str_free_func);
g_list_free_full(list3, _list_str_free_func);
return 0;
}
运行结果
===test copy===
user_data is: list, data is: hello
user_data is: list, data is: world
user_data is: list2, data is: hello
user_data is: list2, data is: world
===test deep copy===
user_data is: list, data is: hello
user_data is: list, data is: world
user_data is: list2, data is: hello
user_data is: list2, data is: world
user_data is: list3, data is: hello
user_data is: list3, data is: world
上述例子看似正常,但使用valgrind检查,会发现内存问题,Invalid free()。
[root@centos7_6 build]# valgrind --tool=memcheck --leak-check=full ./glib_list_copy_err
==5414== Memcheck, a memory error detector
==5414== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==5414== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==5414== Command: ./glib_list_copy_err
==5414==
===test copy===
user_data is: list, data is: hello
user_data is: list, data is: world
user_data is: list2, data is: hello
user_data is: list2, data is: world
===test deep copy===
user_data is: list, data is: hello
user_data is: list, data is: world
user_data is: list2, data is: hello
user_data is: list2, data is: world
user_data is: list3, data is: hello
user_data is: list3, data is: world
==5414== Invalid free() / delete / delete[] / realloc()
==5414== at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5414== by 0x400A2E: _list_str_free_func (glib_list_copy_err.c:15)
==5414== by 0x4E7C657: g_list_foreach (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==5414== by 0x4E7C67A: g_list_free_full (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==5414== by 0x400B89: main (glib_list_copy_err.c:49)
==5414== Address 0x5961040 is 0 bytes inside a block of size 6 free'd
==5414== at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5414== by 0x400A2E: _list_str_free_func (glib_list_copy_err.c:15)
==5414== by 0x4E7C657: g_list_foreach (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==5414== by 0x4E7C67A: g_list_free_full (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==5414== by 0x400B78: main (glib_list_copy_err.c:48)
==5414==
==5414==
==5414== HEAP SUMMARY:
==5414== in use at exit: 2,214 bytes in 9 blocks
==5414== total heap usage: 93 allocs, 86 frees, 400,768 bytes allocated
==5414==
==5414== LEAK SUMMARY:
==5414== definitely lost: 0 bytes in 0 blocks
==5414== indirectly lost: 0 bytes in 0 blocks
==5414== possibly lost: 0 bytes in 0 blocks
==5414== still reachable: 2,214 bytes in 9 blocks
==5414== suppressed: 0 bytes in 0 blocks
==5414== Reachable blocks (those to which a pointer was found) are not shown.
==5414== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==5414==
==5414== For counts of detected and suppressed errors, rerun with: -v
==5414== ERROR SUMMARY: 2 errors from 1 contexts (suppressed: 0 from 0)
原因是list2是浅拷贝过来的,只需要释放链表自身,不需要释放其中的每个节点。如果查看g_list_copy的源码,可以发现list2的每一个节点都是动态申请的新内存,但其中的三个指针(prev,next,data)均指向list节点,并未开辟新的内存。因此,在函数结束时,只需要释放list2的每一个节点,也就是GList结构体的三个指针自身所占的内存,释放list2并不影响list,list的值仍存在。
将g_list_free_full(list2, _list_str_free_func);
改为g_list_free(list2);
即可消除内存错误。
如果是用户自定义的数据,又该如何使用上面的函数呢?
首先定义一个结构体,里面存的是公司员工工号和姓名。
typedef struct employee_info_tag {
gint id;
gchar *name;
}employee_info_t;
员工工号int型就够了,由于员工姓名有长有短,所以这里使用指针来存储不定长度的员工姓名。下面演示插入四个员工信息到链表。
源码见glib_examples\glib_list\glib_list_custom_data_insert
#include
typedef struct employee_info_tag {
gint id;
gchar *name;
}employee_info_t;
static void _list_struct_print_func(gpointer data, gpointer user_data)
{
employee_info_t *einfo = NULL;
einfo = (employee_info_t *)data;
if(NULL == einfo) {
g_print("input param error! \n");
return;
}
g_print("user_data:%s, einfo->id:%d, einfo->name:%s \n", (gchar *)user_data, einfo->id, einfo->name);
}
static void _list_struct_free_func(gpointer data)
{
employee_info_t *einfo = NULL;
einfo = (employee_info_t *)data;
if( (NULL != einfo) && (NULL != einfo->name) ) {
g_free(einfo->name);
}
if(NULL != einfo) {
g_free(einfo);
}
}
gint main(gint argc, gchar **argv)
{
GList *list = NULL;
GList *l = NULL;
employee_info_t *einfo[4] = {NULL, NULL, NULL, NULL};
einfo[0] = g_new0(employee_info_t, 1);
einfo[1] = g_new0(employee_info_t, 1);
einfo[2] = g_new0(employee_info_t, 1);
einfo[3] = g_new0(employee_info_t, 1);
einfo[0]->id = 1000;
einfo[0]->name = g_strdup("Jack");
einfo[1]->id = 1001;
einfo[1]->name = g_strdup("Pony");
einfo[2]->id = 1002;
einfo[2]->name = g_strdup("Robin");
einfo[3]->id = 1003;
einfo[3]->name = g_strdup("NoName");
list = g_list_append(list, (gpointer)einfo[0]); //Jack
list = g_list_prepend(list, (gpointer)einfo[1]); //Pony->Jack
list = g_list_insert(list, (gpointer)einfo[2], 1); //Pony->Robin->Jack
l = g_list_nth(list, 0);
list = g_list_insert_before(list, l, (gpointer)einfo[3]); //NoName->Pony->Robin->Jack
g_list_foreach(list, _list_struct_print_func, "GList");
g_list_free_full(list, _list_struct_free_func);
return 0;
}
运行结果:
user_data:GList, einfo->id:1003, einfo->name:NoName
user_data:GList, einfo->id:1001, einfo->name:Pony
user_data:GList, einfo->id:1002, einfo->name:Robin
user_data:GList, einfo->id:1000, einfo->name:Jack
下面重点分析一下上面链表的内存创建和释放过程。
(请将下面几行逐行复制到文本编辑器并调整字体为等宽字体如宋体、Courier New等再查看。
)
Node1*<---->Node2*<---->Node3*<---->Node4*
├prev ├prev ├prev ├prev
├next ├next ├next ├next
└data* └data* └data* └data*
└einfo[0] └einfo[1] └einfo[2] └einfo[3]
├id(1003) ├id(1001) ├id(1002) ├id(1000)
└name* └name* └name* └name*
上述链表的每个节点都由三部分内存构成,以Node1为例。
上述三部分内存的释放顺序为,先释放name,再释放einfo[0],最后释放Node1,其余节点也按照这个顺序释放,直至整个链表销毁。
由于使用了自定义数据,在操作链表(排序、查找、深拷贝)时,就会用到很多自定义函数,如下所示。
g_list_sort
g_list_find_custom
g_list_copy_deep
下面举例演示。
源码见glib_examples\glib_list\glib_list_custom_data_ops
#include
typedef struct employee_info_tag {
gint id;
gchar *name;
}employee_info_t;
static void _list_struct_print_func(gpointer data, gpointer user_data)
{
employee_info_t *einfo = NULL;
einfo = (employee_info_t *)data;
if(NULL == einfo) {
g_print("input param error! \n");
return;
}
g_print("user_data:%s, einfo->id:%d, einfo->name:%s \n", (gchar *)user_data, einfo->id, einfo->name);
}
static void _list_struct_free_func(gpointer data)
{
employee_info_t *einfo = NULL;
einfo = (employee_info_t *)data;
if( (NULL != einfo) && (NULL != einfo->name) ) {
g_free(einfo->name);
}
if(NULL != einfo) {
g_free(einfo);
}
}
static gint _list_struct_name_cmp_func(gconstpointer a, gconstpointer b)
{
employee_info_t *einfo_a = NULL;
employee_info_t *einfo_b = NULL;
einfo_a = (employee_info_t *)a;
einfo_b = (employee_info_t *)b;
if( (NULL == einfo_a) || (NULL == einfo_b) ) {
g_print("input param error! \n");
return 0;
}
return g_strcmp0(einfo_a->name, einfo_b->name);
}
static gint _list_struct_id_cmp_func(gconstpointer a, gconstpointer b)
{
employee_info_t *einfo_a = NULL;
employee_info_t *einfo_b = NULL;
einfo_a = (employee_info_t *)a;
einfo_b = (employee_info_t *)b;
if( (NULL == einfo_a) || (NULL == einfo_b) ) {
g_print("input param error! \n");
return 0;
}
return (einfo_a->id - einfo_b->id);
}
static gint _list_struct_id_find_func(gconstpointer a, gconstpointer b)
{
employee_info_t *einfo_a = NULL;
gint id;
einfo_a = (employee_info_t *)a;
id = GPOINTER_TO_INT(b);
if (NULL == einfo_a) {
g_print("input param error! \n");
return 0;
}
return (einfo_a->id - id);
}
static gint _list_struct_name_find_func(gconstpointer a, gconstpointer b)
{
employee_info_t *einfo_a = NULL;
gchar *name = NULL;
einfo_a = (employee_info_t *)a;
name = (gchar *)b;
if (NULL == einfo_a) {
g_print("input param error! \n");
return 0;
}
return g_strcmp0(einfo_a->name, name);
}
static gpointer _list_struct_copy_func(gconstpointer src, gpointer data)
{
employee_info_t *einfo = NULL;
einfo = g_new0(employee_info_t, 1);
einfo->id = ((employee_info_t *)src)->id + GPOINTER_TO_INT(data);
einfo->name = g_strdup(((employee_info_t *)src)->name);
return (gpointer)einfo;
}
gint main(gint argc, gchar **argv)
{
GList *list = NULL;
GList *l = NULL;
GList *list2 = NULL;
employee_info_t *einfo[4] = {NULL, NULL, NULL, NULL};
einfo[0] = g_new0(employee_info_t, 1);
einfo[1] = g_new0(employee_info_t, 1);
einfo[2] = g_new0(employee_info_t, 1);
einfo[3] = g_new0(employee_info_t, 1);
einfo[0]->id = 1000;
einfo[0]->name = g_strdup("Jack");
einfo[1]->id = 1001;
einfo[1]->name = g_strdup("Pony");
einfo[2]->id = 1002;
einfo[2]->name = g_strdup("Robin");
einfo[3]->id = 1003;
einfo[3]->name = g_strdup("NoName");
list = g_list_append(list, (gpointer)einfo[0]); //Jack
list = g_list_prepend(list, (gpointer)einfo[1]); //Pony->Jack
list = g_list_insert(list, (gpointer)einfo[2], 1); //Pony->Robin->Jack
l = g_list_nth(list, 0);
list = g_list_insert_before(list, l, (gpointer)einfo[3]); //NoName->Pony->Robin->Jack
g_list_foreach(list, _list_struct_print_func, "ori");
// #sort
list = g_list_sort(list, _list_struct_id_cmp_func);
g_list_foreach(list, _list_struct_print_func, "id sort");
list = g_list_sort(list, _list_struct_name_cmp_func);
g_list_foreach(list, _list_struct_print_func, "name sort");
// #find
l = g_list_find_custom(list, GINT_TO_POINTER(1002), _list_struct_id_find_func);
if( NULL != l) {
g_print("found by id! id=%d, name=%s \n", ((employee_info_t *)l->data)->id, ((employee_info_t *)l->data)->name);
} else {
g_print("can not found by id! \n");
}
l = g_list_find_custom(list, (gpointer)"Jack", _list_struct_name_find_func);
if( NULL != l) {
g_print("found by name! id=%d, name=%s \n", ((employee_info_t *)l->data)->id, ((employee_info_t *)l->data)->name);
} else {
g_print("cat not found by name! \n");
}
// #copy
list2 = g_list_copy_deep(list, _list_struct_copy_func, GINT_TO_POINTER(5000));
g_list_foreach(list2, _list_struct_print_func, "list2");
g_list_free_full(list2, _list_struct_free_func);
g_list_free_full(list, _list_struct_free_func);
return 0;
}
运行结果:
user_data:ori, einfo->id:1003, einfo->name:NoName
user_data:ori, einfo->id:1001, einfo->name:Pony
user_data:ori, einfo->id:1002, einfo->name:Robin
user_data:ori, einfo->id:1000, einfo->name:Jack
user_data:id sort, einfo->id:1000, einfo->name:Jack
user_data:id sort, einfo->id:1001, einfo->name:Pony
user_data:id sort, einfo->id:1002, einfo->name:Robin
user_data:id sort, einfo->id:1003, einfo->name:NoName
user_data:name sort, einfo->id:1000, einfo->name:Jack
user_data:name sort, einfo->id:1003, einfo->name:NoName
user_data:name sort, einfo->id:1001, einfo->name:Pony
user_data:name sort, einfo->id:1002, einfo->name:Robin
found by id! id=1002, name=Robin
found by name! id=1000, name=Jack
user_data:list2, einfo->id:6000, einfo->name:Jack
user_data:list2, einfo->id:6003, einfo->name:NoName
user_data:list2, einfo->id:6001, einfo->name:Pony
user_data:list2, einfo->id:6002, einfo->name:Robin
分析:
创建一个带有自定义节点的链表,先根据员工工号进行排序,再根据员工姓名进行排序,再通过员工工号进行查找,再通过员工姓名进行查找,最后深拷贝一个链表,同时把链表中的所有员工工号增加5000。
思考:
如果上述数据结构,name不是一个字符串指针,而是一个固定长度的数组呢?比name是指针处理起来复杂还是简单,如何实现?
typedef struct employee_info_tag {
gint id;
gchar name[32];
}employee_info_t;
前面在讲链表拷贝时提到链表中的节点不能是局部变量,那么如果是局部变量,会导致什么后果呢,下面通过例子演示一下效果。
源码见glib_examples\glib_list\glib_list_mem
#include
GList *global_list = NULL;
static void _list_str_print_func(gpointer data, gpointer user_data)
{
g_print("user_data is: %s, data is: %s \n", (gchar *)user_data, (gchar*)data);
}
static void _list_local_var_str_append(void)
{
gchar str1[32] = {0};
gchar str2[32] = {0};
g_strlcpy(str1, "hello", sizeof("hello"));
g_strlcpy(str2, "world", sizeof("world"));
global_list = g_list_append(global_list, (gpointer)str1);
global_list = g_list_append(global_list, (gpointer)str2);
}
static void _list_const_var_str_append(void)
{
gchar *str1 = "hello";
gchar *str2 = "world";
global_list = g_list_append(global_list, (gpointer)str1);
global_list = g_list_append(global_list, (gpointer)str2);
}
static void _list_heap_var_str_append(void)
{
gchar *str1 = g_strdup("hello");
gchar *str2 = g_strdup("world");
global_list = g_list_append(global_list, (gpointer)str1);
global_list = g_list_append(global_list, (gpointer)str2);
}
static void _list_heap_var_str_free_func(gpointer data)
{
g_free((gchar *)data);
}
gint main(gint argc, gchar **argv)
{
_list_local_var_str_append();
g_list_foreach(global_list, _list_str_print_func, "local var");
g_list_free(global_list);
global_list = NULL;
_list_const_var_str_append();
g_list_foreach(global_list, _list_str_print_func, "const var");
g_list_free(global_list);
global_list = NULL;
_list_heap_var_str_append();
g_list_foreach(global_list, _list_str_print_func, "heap var");
g_list_free_full(global_list, _list_heap_var_str_free_func);
global_list = NULL;
return 0;
}
运行结果:
[Invalid UTF-8] user_data is: local var, data is: \xee\xf2
[Invalid UTF-8] user_data is: local var, data is: \xa4\x0d@
user_data is: const var, data is: hello
user_data is: const var, data is: world
user_data is: heap var, data is: hello
user_data is: heap var, data is: world
分析:在栈(局部变量)、静态存储区(字符串)和堆(malloc申请)上分别申请内存,并将这三种内存类型的数据加入链表。结合上面的运行结果,可以得出如下结论: