简介
单向链表是一种常见的数据结构,它只有一个后向指针,结构简单。
数据结构
GSList 包含一个next指针和一个gpointer(实际上是void *
)类型的数据data。
struct _GSList
{
gpointer data;
GSList *next;
};
函数列表
GSList * g_slist_alloc ()
GSList * g_slist_append ()
GSList * g_slist_prepend ()
GSList * g_slist_insert ()
GSList * g_slist_insert_before ()
GSList * g_slist_insert_sorted ()
GSList * g_slist_remove ()
GSList * g_slist_remove_link ()
GSList * g_slist_delete_link ()
GSList * g_slist_remove_all ()
void g_slist_free ()
void g_slist_free_full ()
void g_slist_free_1 ()
guint g_slist_length ()
GSList * g_slist_copy ()
GSList * g_slist_copy_deep ()
GSList * g_slist_reverse ()
GSList * g_slist_insert_sorted_with_data ()
GSList * g_slist_sort ()
GSList * g_slist_sort_with_data ()
GSList * g_slist_concat ()
void g_slist_foreach ()
GSList * g_slist_last ()
#define g_slist_next()
GSList * g_slist_nth ()
gpointer g_slist_nth_data ()
GSList * g_slist_find ()
GSList * g_slist_find_custom ()
gint g_slist_position ()
gint g_slist_index ()
函数功能分类
插入
// 头插
g_slist_append
// 尾插
g_slist_prepend
// 注意:返回的是新链表首地址,但此时旧链表其实也已经发生变化了 g_slist_insert(slist, GINT_TO_POINTER(70), 3);
g_slist_insert
//在第二个参数的节点之前添加一个节点, slist = g_slist_insert_before(slist, slist->next, GINT_TO_POINTER(32));
g_slist_insert_before
查找
// 原型GSList *g_slist_last (GSList *list);单链表遍历到尾部获取到最后一个节点
g_slist_last
// 原型#define g_slist_next(slist) 其实现方式为直接获取slist的下一个节点
g_slist_next
// 原型GSList *g_slist_nth (GSList *list, guint n);查找第n个节点,节点从0开始计数
g_slist_nth
// 原型gpointer g_slist_nth_data (GSList *list, guint n);同g_slist_nth_data,返回的是节点的数据而非整个节点
g_slist_nth_data
// 原型GSList *g_slist_find (GSList *list, gconstpointer data); 节点查找
g_slist_find
// 原型GSList *g_slist_find_custom (GSList *list, gconstpointer data, GCompareFunc func); 节点查找,自定义查找方式
g_slist_find_custom
// 原型 gint g_slist_position (GSList *list, GSList *llink); 返回某一节点的位置,在对比时由于采用的是list==llink方式,因此对于自定义数据无法支持,需要自己扩充实现一个g_slist_position_custom函数
g_slist_position
// 原型gint g_slist_index (GSList *list, gconstpointer data); 根据节点值返回某一节点在链表中的位置,该函数不能很好地支持自定义数据,需要扩充一个g_slist_index_custom函数
g_slist_index
删除
// 以值方式从链表中删除一个节点,如果链表中有多个节点相同的值,则只删除第一个
g_slist_remove
// 以值方式从链表中删除一个节点,如果有多个节点相同的值则会全部删除
g_slist_remove_all
// 以节点方式从链表中删除一个节点,但并不会释放该节点的内存
g_slist_remove_link
// 以节点方式从链表中删除一个节点,并且该节点的内存会被自动释放
s_slist_delete_link
释放
// 释放一个链表,如果节点有手动分配的内存,则需要调用g_slist_free_full函数自行释放。单链表没有创建,但append,prepend,insert动作均暗含创建操作,因此只要链表不为空,就需要调用本接口释放该链表
g_slist_free
// 释放一个链表,且自定义释放节点中的内存
g_slist_free_full
// 释放一个节点,该节点往往来自g_slist_remove_link,因为后者不会自动释放内存
g_slist_free_1
排序
// 原型GSList *g_slist_sort (GSList *list, GCompareFunc compare_func);使用给定的比较函数对链表进行排序
g_slist_sort
// 原型GSList *g_slist_sort_with_data (GSList *list, GCompareDataFunc compare_func, gpointer user_data); 使用给定的比较函数对链表进行排序,在比较时接受用户传入数据
g_slist_sort_with_data
// 原型GSList *g_slist_insert_sorted (GSList *list, gpointer data, GCompareFunc func);和排序的区别是可以插入一个数据节点
g_slist_insert_sorted
// 原型GSList *g_slist_insert_sorted_with_data (GSList *list, gpointer data, GCompareDataFunc func, gpointer user_data);和g_slist_sort_with_data非常相似
g_slist_insert_sorted_with_data
复制
// 链表浅拷贝,如果节点有手动分配的内存,则必须使用深拷贝
g_slist_copy
// 链表深拷贝,拷贝的具体实现函数由使用者提供。使用本函数拷贝的链表,必须使用g_slist_free_full释放链表节点
g_slist_copy_deep
反转
// 反转链表,slist = g_slist_reverse(slist);返回值可以返回给原链表头
g_slist_reverse
合并
// 将两个链表合并成一个链表,合并后的链表可以直接释放
g_slist_concat
遍历
// 遍历方法自己实现,原型为void(*GFunc)(gpointer data, gpointer user_data);第一个参数为GSList节点的data元素,第二个参数由g_slist_foreach传入
g_slist_foreach
测长
// 链表中节点的个数
g_slist_length
函数功能说明及综合演示
单链表插入
下面演示单链表插入操作,每个待插入的节点都是一个固定长度的结构体。
源码见glib_examples\glib_slist\glib_slist_append_struct
示例程序:
#include <glib.h>
typedef struct mydata_tag {
int id;
char name[32];
}my_data_t;
void my_printf(gpointer data, gpointer user_data)
{
g_print("my_printf: id=%d, name=%s, user_data=%s\n", ((my_data_t *)data)->id, ((my_data_t *)data)->name, (char *)user_data);
}
static void test_slist_append_struct(void)
{
int i;
GSList *slist = NULL;
GSList *s;
my_data_t mydata[16];
memset(mydata, 0, sizeof(my_data_t)*5);
for(i=0;i<5;i++) {
mydata[i].id = i;
g_snprintf(mydata[i].name, 16, "data is %d", i);
slist = g_slist_append(slist, (gpointer)&mydata[i]);
}
g_print("\n");
s = slist;
while (NULL != s) {
g_printf("the slist data: %d, %s \n", ((my_data_t *)(s->data))->id, ((my_data_t *)(s->data))->name);
s = s->next;
}
g_slist_foreach(slist, my_printf, "user_data_str");
g_slist_free(slist);
}
gint main (gint argc, gchar *argv[])
{
gint i;
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/slist/append", test_slist_append_struct);
return g_test_run ();
}
运行结果:
[root@centos7_6 build]# ./glib_slist_append_struct
/slist/append:
the slist data: 0, data is 0
the slist data: 1, data is 1
the slist data: 2, data is 2
the slist data: 3, data is 3
the slist data: 4, data is 4
my_printf: id=0, name=data is 0, user_data=user_data_str
my_printf: id=1, name=data is 1, user_data=user_data_str
my_printf: id=2, name=data is 2, user_data=user_data_str
my_printf: id=3, name=data is 3, user_data=user_data_str
my_printf: id=4, name=data is 4, user_data=user_data_str
OK
单链表释放
g_slist_free
可以释放单链表,但如果单链表节点有手动申请的内存,就需要调用
g_slist_free_full
函数,传入一个节点释放函数,对每个节点都调用一遍该释放函数。
源码见glib_examples\glib_slist\glib_slist_free
示例程序
#include <glib.h>
typedef struct mydata_tag {
int id;
char *name;
}my_data_t;
void my_struct_free_func(gpointer data)
{
my_data_t *mydata = (my_data_t *)data;
g_return_if_fail(mydata);
if(NULL != mydata->name) {
g_printf("free mydata->name: %s \n", mydata->name);
free(mydata->name);
}
return;
}
void my_struct_printf(gpointer data, gpointer user_data)
{
g_printf("%s: id=%d, name=%s, user_data=%s\n", __FUNCTION__,
((my_data_t *)data)->id, ((my_data_t *)data)->name, (char *)user_data);
}
static void test_slist_free(void)
{
int i = 0;
GSList *slist = NULL;
my_data_t mydata[16];
char name[16] = {0};
memset(mydata, 0, sizeof(my_data_t)*5);
for(i=0;i<5;i++) {
mydata[i].id = i;
g_snprintf(name, 16, "data is %d", i);
mydata[i].name = strdup(name);
slist = g_slist_append(slist, (gpointer)&mydata[i]);
}
g_printf("\n");
g_slist_foreach(slist, my_struct_printf, "my_struct_printf");
g_slist_free_full(slist, my_struct_free_func);
}
gint main (gint argc, gchar *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/slist/free", test_slist_free);
return g_test_run ();
}
运行结果:
[root@centos7_6 build]# ./glib_slist_free
/slist/free:
my_struct_printf: id=0, name=data is 0, user_data=my_struct_printf
my_struct_printf: id=1, name=data is 1, user_data=my_struct_printf
my_struct_printf: id=2, name=data is 2, user_data=my_struct_printf
my_struct_printf: id=3, name=data is 3, user_data=my_struct_printf
my_struct_printf: id=4, name=data is 4, user_data=my_struct_printf
free mydata->name: data is 0
free mydata->name: data is 1
free mydata->name: data is 2
free mydata->name: data is 3
free mydata->name: data is 4
OK
单链表查找
由于单链表的节点包含的数据类型不固定,可以是整型、字符串甚至是结构体,如果想精确查找到某一节点,需要用户自行提供节点的比较函数。下面演示单链表的查找功能。
源码见glib_examples\glib_slist\glib_slist_find_cmp_func
示例程序
#include <glib.h>
typedef struct mydata_tag {
int id;
char *name;
}my_data_t;
void my_struct_free_func(gpointer data)
{
my_data_t *mydata = NULL;
mydata = (my_data_t *)data;
if(NULL != mydata->name) {
//g_print("free mydata->name: %s \n", mydata->name);
free(mydata->name);
}
return;
}
void my_struct_printf(gpointer data, gpointer user_data)
{
g_printf("%s: id=%d, name=%s, user_data=%s\n", __FUNCTION__,
((my_data_t *)data)->id, ((my_data_t *)data)->name, (char *)user_data);
}
gint my_cmp_func(gconstpointer a, gconstpointer b)
{
my_data_t *node = NULL;
node = (my_data_t *)a;
//g_print("node->name: %s, b %s \n", node->name, (gchar *)b);
return strcmp(node->name, b);
}
static void test_slist_find_custom_cmp(void)
{
int i = 0;
GSList *slist = NULL;
GSList *slink = NULL;
my_data_t *snode = NULL;
my_data_t mydata[16];
char name[16] = {0};
memset(mydata, 0, sizeof(my_data_t)*5);
for(i=0;i<5;i++) {
mydata[i].id = i;
g_snprintf(name, 16, "data is %d", i);
mydata[i].name = strdup(name);
slist = g_slist_append(slist, (gpointer)&mydata[i]);
}
g_print("\n");
g_print("len=%d \n", g_slist_length(slist));
g_slist_foreach(slist, my_struct_printf, "my_struct_printf");
slink = g_slist_nth(slist, 3);
g_print("slink->data->id=%d \n", ((my_data_t *)slink->data)->id);
snode = g_slist_nth_data(slist, 3);
g_print("snode->id=%d \n", snode->id);
slink = g_slist_find_custom(slist, "data is 3", my_cmp_func);
if(NULL == slink) {
g_print("data not found! \n");
} else {
g_print("found! %s \n", ((my_data_t *)(slink->data))->name);
}
g_slist_free_full(slist, my_struct_free_func);
}
gint main (gint argc, gchar *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/slist/find_custom_cmp", test_slist_find_custom_cmp);
return g_test_run ();
}
运行结果:
[root@centos7_6 build]# ./glib_slist_find_cmp_func
/slist/find_custom_cmp:
len=5
my_struct_printf: id=0, name=data is 0, user_data=my_struct_printf
my_struct_printf: id=1, name=data is 1, user_data=my_struct_printf
my_struct_printf: id=2, name=data is 2, user_data=my_struct_printf
my_struct_printf: id=3, name=data is 3, user_data=my_struct_printf
my_struct_printf: id=4, name=data is 4, user_data=my_struct_printf
slink->data->id=3
snode->id=3
found! data is 3
OK
单链表删除节点
将一个节点从单链表删除。
源码见glib_examples\glib_slist\glib_slist_remove
示例程序
#include <glib.h>
void my_int_printf(gpointer data, gpointer user_data)
{
g_printf("%s data:%d \n", (char*)user_data, GPOINTER_TO_INT(data));
return;
}
static void test_slist_remove(void)
{
int i = 0;
GSList *slist = NULL;
GSList *clist = NULL;
for(i=0;i<5;i++) {
slist = g_slist_append(slist, GINT_TO_POINTER(i));
}
slist = g_slist_insert_before(slist, slist->next, GINT_TO_POINTER(32));
g_printf("\n");
g_slist_foreach(slist, my_int_printf, "before remove");
slist = g_slist_remove(slist, GINT_TO_POINTER(32));
g_printf("\n");
g_slist_foreach(slist, my_int_printf, "after remove");
g_slist_free(slist);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/slist/remove", test_slist_remove);
return g_test_run ();
}
运行结果:
[root@centos7_6 build]# ./glib_slist_remove
/slist/remove:
before remove data:0
before remove data:32
before remove data:1
before remove data:2
before remove data:3
before remove data:4
after remove data:0
after remove data:1
after remove data:2
after remove data:3
after remove data:4
OK
单链表删除值相同的所有节点
上一个演示程序演示的是如何将一个节点从单链表删除,如果单链表有多个值相同的节点,则只会删除第一个,而使用g_slist_remove_all函数可以将单链表中所有值相同的节点都删除。
源码见glib_examples\glib_slist\glib_slist_remove_all
示例程序如下:
#include <glib.h>
void my_int_printf(gpointer data, gpointer user_data)
{
g_printf("%s data:%d \n", (char*)user_data, GPOINTER_TO_INT(data));
return;
}
static void test_slist_remove_all(void)
{
int i = 0;
GSList *slist = NULL;
GSList *clist = NULL;
for(i=0;i<5;i++) {
slist = g_slist_append(slist, GINT_TO_POINTER(i));
}
slist = g_slist_insert_before(slist, slist->next, GINT_TO_POINTER(32));
slist = g_slist_insert_before(slist, slist->next->next->next->next, GINT_TO_POINTER(32));
g_printf("\n");
g_slist_foreach(slist, my_int_printf, "before remove all");
slist = g_slist_remove_all(slist, GINT_TO_POINTER(32));
g_printf("\n");
g_slist_foreach(slist, my_int_printf, "after remove all");
g_slist_free(slist);
}
gint main (gint argc, gchar *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/slist/remove_all", test_slist_remove_all);
return g_test_run ();
}
运行结果:
[root@centos7_6 build]# ./glib_slist_remove_all
/slist/remove_all:
before remove all data:0
before remove all data:32
before remove all data:1
before remove all data:2
before remove all data:32
before remove all data:3
before remove all data:4
after remove all data:0
after remove all data:1
after remove all data:2
after remove all data:3
after remove all data:4
OK
单链表逆序
将一个单链表逆序。
源码见glib_examples\glib_slist\glib_slist_reverse
示例程序
#include <glib.h>
void my_int_printf(gpointer data, gpointer user_data)
{
g_printf("%s: value=%d user_data=%s\n", __FUNCTION__, *(int *)(data), (char *)user_data);
}
static void test_slist_reverse (void)
{
GSList *slist = NULL;
GSList *st;
gint nums[5] = { 1, 2, 3, 4, 5};
gint i;
for (i = 0; i < 5; i++) {
slist = g_slist_append (slist, &nums[i]);
}
g_print("\n");
g_slist_foreach(slist, my_int_printf, "before reverse");
slist = g_slist_reverse (slist);
g_slist_foreach(slist, my_int_printf, "after reverse");
g_slist_free (slist);
}
gint main (gint argc, gchar *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/slist/reverse", test_slist_reverse);
return g_test_run ();
}
运行结果:
[root@centos7_6 build]# ./glib_slist_reverse
/slist/reverse:
my_int_printf: value=1 user_data=before reverse
my_int_printf: value=2 user_data=before reverse
my_int_printf: value=3 user_data=before reverse
my_int_printf: value=4 user_data=before reverse
my_int_printf: value=5 user_data=before reverse
my_int_printf: value=5 user_data=after reverse
my_int_printf: value=4 user_data=after reverse
my_int_printf: value=3 user_data=after reverse
my_int_printf: value=2 user_data=after reverse
my_int_printf: value=1 user_data=after reverse
OK
单链表排序-节点类型为整型
将一个单链表排序,其节点类型为整型。
源码见glib_examples\glib_slist\glib_slist_sort_int
示例程序
#include <glib.h>
#define SIZE 5
#define NUMBER_MIN 100
#define NUMBER_MAX 200
static guint32 array[SIZE];
void my_int_printf(gpointer data, gpointer user_data)
{
g_printf("%s: value=%d user_data=%s\n", __FUNCTION__, GPOINTER_TO_INT(data), (char *)user_data);
}
gint my_int_cmp_func (gconstpointer d1, gconstpointer d2)
{
gint i1, i2;
i1 = GPOINTER_TO_INT (d1);
i2 = GPOINTER_TO_INT (d2);
return i1 - i2;
}
static void test_slist_sort_int(void)
{
gint i = 0;
GSList *slist = NULL;
for(i=0;i<SIZE;i++) {
slist = g_slist_append(slist, GINT_TO_POINTER(array[i]));
}
g_printf("\n");
g_printf("len=%d \n", g_slist_length(slist));
g_slist_foreach(slist, my_int_printf, "before sort");
slist = g_slist_sort(slist, my_int_cmp_func);
g_slist_foreach(slist, my_int_printf, "after sort");
g_slist_free(slist);
}
gint main (gint argc, gchar *argv[])
{
gint i;
g_test_init (&argc, &argv, NULL);
for (i = 0; i < SIZE; i++) {
array[i] = g_test_rand_int_range (NUMBER_MIN, NUMBER_MAX);
}
g_test_add_func ("/slist/sort_int", test_slist_sort_int);
return g_test_run ();
}
运行结果:
[root@centos7_6 build]# ./glib_slist_sort_int/glib_slist_sort_int
/slist/sort_int:
len=5
my_int_printf: value=115 user_data=before sort
my_int_printf: value=190 user_data=before sort
my_int_printf: value=133 user_data=before sort
my_int_printf: value=197 user_data=before sort
my_int_printf: value=161 user_data=before sort
my_int_printf: value=115 user_data=after sort
my_int_printf: value=133 user_data=after sort
my_int_printf: value=161 user_data=after sort
my_int_printf: value=190 user_data=after sort
my_int_printf: value=197 user_data=after sort
OK
单链表排序-节点类型为结构体类型
将一个单链表排序,其节点类型为结构体类型。
源码见glib_examples\glib_slist\glib_slist_sort_struct
示例程序
#include <glib.h>
#define SIZE 5
#define NUMBER_MIN 100
#define NUMBER_MAX 200
static guint32 array[SIZE];
typedef struct mydata_tag {
int id;
char *name;
}my_data_t;
void my_struct_free_func(gpointer data)
{
my_data_t *mydata = NULL;
mydata = (my_data_t *)data;
if(NULL != mydata->name) {
//g_printf("free mydata->name: %s \n", mydata->name);
free(mydata->name);
}
return;
}
void my_struct_printf(gpointer data, gpointer user_data)
{
g_printf("%s: id=%d, name=%s, user_data=%s\n", __FUNCTION__,
((my_data_t *)data)->id, ((my_data_t *)data)->name, (char *)user_data);
}
gint my_int_cmp_func (gconstpointer d1, gconstpointer d2)
{
gint i1, i2;
i1 = GPOINTER_TO_INT (d1);
i2 = GPOINTER_TO_INT (d2);
return i1 - i2;
}
gint my_struct_cmp_func (gconstpointer d1, gconstpointer d2)
{
return ((my_data_t *)d1)->id - ((my_data_t *)d2)->id;
}
static void test_slist_sort_struct(void)
{
gint i = 0;
GSList *slist = NULL;
my_data_t mydata[16];
char name[16] = {0};
memset(mydata, 0, sizeof(my_data_t)*SIZE);
for(i=0;i<SIZE;i++) {
mydata[i].id = array[i];
g_snprintf(name, 16, "data is %d", array[i]);
mydata[i].name = strdup(name);
slist = g_slist_append(slist, (gpointer)&mydata[i]);
}
g_print("\n");
g_print("len=%d \n", g_slist_length(slist));
g_slist_foreach(slist, my_struct_printf, "before sort");
slist = g_slist_sort(slist, my_struct_cmp_func);
g_slist_foreach(slist, my_struct_printf, "after sort");
g_slist_free_full(slist, my_struct_free_func);
}
gint main (gint argc, gchar *argv[])
{
gint i;
g_test_init (&argc, &argv, NULL);
for (i = 0; i < SIZE; i++) {
array[i] = g_test_rand_int_range (NUMBER_MIN, NUMBER_MAX);
}
g_test_add_func ("/slist/sort_struct", test_slist_sort_struct);
return g_test_run ();
}
运行结果:
[root@centos7_6 build]# ./glib_slist_sort_struct/glib_slist_sort_struct
/slist/sort_struct:
len=5
my_struct_printf: id=107, name=data is 107, user_data=before sort
my_struct_printf: id=177, name=data is 177, user_data=before sort
my_struct_printf: id=176, name=data is 176, user_data=before sort
my_struct_printf: id=131, name=data is 131, user_data=before sort
my_struct_printf: id=138, name=data is 138, user_data=before sort
my_struct_printf: id=107, name=data is 107, user_data=after sort
my_struct_printf: id=131, name=data is 131, user_data=after sort
my_struct_printf: id=138, name=data is 138, user_data=after sort
my_struct_printf: id=176, name=data is 176, user_data=after sort
my_struct_printf: id=177, name=data is 177, user_data=after sort
OK
单链表排序-自定义排序函数
如果节点是一个结构体数据类型,那就可以按照结构体的任意成员变量排序,此时,需要调用者自定义排序函数。
源码见glib_examples\glib_slist\glib_slist_sort_with_data_struct
示例程序
#include <glib.h>
#define SIZE 5
#define NUMBER_MIN 100
#define NUMBER_MAX 200
static guint32 array[SIZE];
typedef struct mydata_tag {
int id;
char *name;
}my_data_t;
void my_struct_free_func(gpointer data)
{
my_data_t *mydata = NULL;
mydata = (my_data_t *)data;
if(NULL != mydata->name) {
//g_printf("free mydata->name: %s \n", mydata->name);
free(mydata->name);
}
return;
}
void my_struct_printf(gpointer data, gpointer user_data)
{
g_printf("%s: id=%d, name=%s, user_data=%s\n", __FUNCTION__,
((my_data_t *)data)->id, ((my_data_t *)data)->name, (char *)user_data);
}
gint my_int_cmp_func (gconstpointer d1, gconstpointer d2)
{
gint i1, i2;
i1 = GPOINTER_TO_INT (d1);
i2 = GPOINTER_TO_INT (d2);
return i1 - i2;
}
gint my_struct_cmp_data_func (gconstpointer d1, gconstpointer d2, gpointer user_data)
{
gint ret = 0;
if(0 == strcmp((char *)user_data, "id")) {
ret = ((my_data_t *)d1)->id - ((my_data_t *)d2)->id;
}
if(0 == strcmp((char *)user_data, "name")) {
ret = strcmp(((my_data_t *)d1)->name , ((my_data_t *)d2)->name);
}
return ret;
}
static void test_slist_sort_with_data_struct(void)
{
gint i = 0;
GSList *slist = NULL;
my_data_t mydata[16];
char name[16] = {0};
memset(mydata, 0, sizeof(my_data_t)*SIZE);
for(i=0;i<SIZE;i++) {
mydata[i].id = array[i];
g_snprintf(name, 16, "data is %d", array[i]);
mydata[i].name = strdup(name);
slist = g_slist_append(slist, (gpointer)&mydata[i]);
}
g_print("\n");
g_print("len=%d \n", g_slist_length(slist));
g_slist_foreach(slist, my_struct_printf, "before sort with name");
slist = g_slist_sort_with_data(slist, my_struct_cmp_data_func, "name");
g_slist_foreach(slist, my_struct_printf, "after sort with name");
g_slist_free_full(slist, my_struct_free_func);
}
gint main (gint argc, gchar *argv[])
{
gint i;
g_test_init (&argc, &argv, NULL);
for (i = 0; i < SIZE; i++) {
array[i] = g_test_rand_int_range (NUMBER_MIN, NUMBER_MAX);
}
g_test_add_func ("/slist/sort_with_data_struct", test_slist_sort_with_data_struct);
return g_test_run ();
}
运行结果:
[root@centos7_6 build]# ./glib_slist_sort_with_data_struct
/slist/sort_with_data_struct:
len=5
my_struct_printf: id=186, name=data is 186, user_data=before sort with name
my_struct_printf: id=131, name=data is 131, user_data=before sort with name
my_struct_printf: id=186, name=data is 186, user_data=before sort with name
my_struct_printf: id=165, name=data is 165, user_data=before sort with name
my_struct_printf: id=183, name=data is 183, user_data=before sort with name
my_struct_printf: id=131, name=data is 131, user_data=after sort with name
my_struct_printf: id=165, name=data is 165, user_data=after sort with name
my_struct_printf: id=183, name=data is 183, user_data=after sort with name
my_struct_printf: id=186, name=data is 186, user_data=after sort with name
my_struct_printf: id=186, name=data is 186, user_data=after sort with name
OK
单链表浅拷贝
单链表浅拷贝时新链表的各个节点指针指向原节点地址,新节点和原节点共用同一段内存。下面演示说明。
下面示例程序,演示了浅拷贝下,如果释放链表,会出现内存重复释放的段错误提示。
源码见glib_examples\glib_slist\glib_slist_copy_double_free_err
示例程序
#include <glib.h>
typedef struct mydata_tag {
int id;
char *name;
}my_data_t;
void my_free_func(gpointer data)
{
my_data_t *mydata = NULL;
mydata = (my_data_t *)data;
if(NULL != mydata->name) {
g_printf("free mydata->name: %s \n", mydata->name);
free(mydata->name);
}
return;
}
void my_printf(gpointer data, gpointer user_data)
{
g_printf("my_printf: id=%d, name=%s, user_data=%s\n", ((my_data_t *)data)->id, ((my_data_t *)data)->name, (char *)user_data);
}
static void test_slist_copy_double_free_err(void)
{
GSList *slist = NULL;
GSList *clist = NULL;
GSList *s;
my_data_t mydata[16];
char name[16] = {0};
int i;
memset(mydata, 0, sizeof(my_data_t)*5);
for(i=0;i<5;i++) {
mydata[i].id = i;
g_snprintf(name, 16, "data is %d", i);
mydata[i].name = strdup(name);
slist = g_slist_append(slist, (gpointer)&mydata[i]);
}
g_slist_foreach(slist, my_printf, "slist");
clist = g_slist_copy(slist);
g_slist_foreach(clist, my_printf, "clist");
g_slist_free_full(slist, my_free_func);
g_slist_free_full(clist, my_free_func);
//g_slist_free(clist);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/slist/copy_double_free_err", test_slist_copy_double_free_err);
return g_test_run ();
}
运行结果:
[root@centos7_6 build]# ./glib_slist_copy_double_free_err
/slist/copy_double_free_err: my_printf: id=0, name=data is 0, user_data=slist
my_printf: id=1, name=data is 1, user_data=slist
my_printf: id=2, name=data is 2, user_data=slist
my_printf: id=3, name=data is 3, user_data=slist
my_printf: id=4, name=data is 4, user_data=slist
my_printf: id=0, name=data is 0, user_data=clist
my_printf: id=1, name=data is 1, user_data=clist
my_printf: id=2, name=data is 2, user_data=clist
my_printf: id=3, name=data is 3, user_data=clist
my_printf: id=4, name=data is 4, user_data=clist
free mydata->name: data is 0
free mydata->name: data is 1
free mydata->name: data is 2
free mydata->name: data is 3
free mydata->name: data is 4
free mydata->name:
free mydata->name: P?)
free mydata->name: p?)
free mydata->name: ▒?)
free mydata->name: ▒?)
段错误(吐核)
原因是,浅拷贝情况下,新节点指向的是原节点的地址,两个节点共用一段内存,并没有开辟新的内存空间,因此第一个链表释放完,节点的地址已经不存在了,第二个节点再释放,就会导致内存重复释放。修改为如下程序即可。
源码见glib_examples\glib_slist\glib_slist_copy_free
示例程序
#include <glib.h>
typedef struct mydata_tag {
int id;
char *name;
}my_data_t;
void my_free_func(gpointer data)
{
my_data_t *mydata = NULL;
mydata = (my_data_t *)data;
if(NULL != mydata->name) {
g_printf("free mydata->name: %s \n", mydata->name);
free(mydata->name);
}
return;
}
void my_printf(gpointer data, gpointer user_data)
{
g_printf("my_printf: id=%d, name=%s, user_data=%s\n", ((my_data_t *)data)->id, ((my_data_t *)data)->name, (char *)user_data);
}
static void test_slist_copy_free(void)
{
GSList *slist = NULL;
GSList *clist = NULL;
GSList *s;
my_data_t mydata[16];
char name[16] = {0};
int i;
memset(mydata, 0, sizeof(my_data_t)*5);
for(i=0;i<5;i++) {
mydata[i].id = i;
g_snprintf(name, 16, "data is %d", i);
mydata[i].name = strdup(name);
slist = g_slist_append(slist, (gpointer)&mydata[i]);
}
g_slist_foreach(slist, my_printf, "slist");
clist = g_slist_copy(slist);
g_slist_foreach(clist, my_printf, "clist");
g_slist_free_full(slist, my_free_func);
//g_slist_free_full(clist, my_free_func);
g_slist_free(clist);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/slist/copy_free", test_slist_copy_free);
return g_test_run ();
}
运行结果:
[root@centos7_6 build]# ./glib_slist_copy_free
/slist/copy_free: my_printf: id=0, name=data is 0, user_data=slist
my_printf: id=1, name=data is 1, user_data=slist
my_printf: id=2, name=data is 2, user_data=slist
my_printf: id=3, name=data is 3, user_data=slist
my_printf: id=4, name=data is 4, user_data=slist
my_printf: id=0, name=data is 0, user_data=clist
my_printf: id=1, name=data is 1, user_data=clist
my_printf: id=2, name=data is 2, user_data=clist
my_printf: id=3, name=data is 3, user_data=clist
my_printf: id=4, name=data is 4, user_data=clist
free mydata->name: data is 0
free mydata->name: data is 1
free mydata->name: data is 2
free mydata->name: data is 3
free mydata->name: data is 4
OK
单链表深拷贝
单链表浅拷贝时只拷贝链表各节点的地址,如果节点地址不连续,比如,存在指针的情况,则该指针指向的内存不会被拷贝,下面演示说明。
源码见glib_examples\glib_slist\glib_slist_copy_free
示例程序
#include <glib.h>
typedef struct mydata_tag {
int id;
char *name;
}my_data_t;
void my_free_func(gpointer data)
{
my_data_t *mydata = NULL;
mydata = (my_data_t *)data;
if(NULL != mydata->name) {
//g_printf("free mydata->name: %s \n", mydata->name);
g_free(mydata->name);
}
return;
}
void my_printf(gpointer data, gpointer user_data)
{
g_printf("my_printf: id=%d, name=%s, user_data=%s\n", ((my_data_t *)data)->id, ((my_data_t *)data)->name, (char *)user_data);
}
gpointer my_deep_copy_func(gconstpointer src, gpointer data)
{
char new_name[16] = {0};
my_data_t *src_data = (my_data_t *)src;
my_data_t *mydata = (my_data_t *)g_malloc(sizeof(my_data_t));
mydata->id = src_data->id;
g_snprintf(new_name, 16, "[new]%s", src_data->name);
mydata->name = g_strdup(new_name);
return mydata;
}
static void test_slist_deep_copy(void)
{
GSList *slist = NULL;
GSList *clist = NULL;
GSList *s;
my_data_t mydata[16];
char name[16] = {0};
int i;
memset(mydata, 0, sizeof(my_data_t)*5);
for(i=0;i<5;i++) {
mydata[i].id = i;
g_snprintf(name, 16, "data is %d", i);
mydata[i].name = g_strdup(name);
slist = g_slist_append(slist, (gpointer)&mydata[i]);
}
g_slist_foreach(slist, my_printf, "slist");
clist = g_slist_copy_deep(slist, my_deep_copy_func, NULL);
g_slist_foreach(clist, my_printf, "clist");
g_slist_free_full(slist, my_free_func);
g_slist_free_full(clist, my_free_func);
//g_slist_free(clist);
}
gint main (gint argc, gchar *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/slist/deep_copy", test_slist_deep_copy);
return g_test_run ();
}
运行结果:
[root@centos7_6 build]# ./glib_slist_deep_copy
/slist/deep_copy: my_printf: id=0, name=data is 0, user_data=slist
my_printf: id=1, name=data is 1, user_data=slist
my_printf: id=2, name=data is 2, user_data=slist
my_printf: id=3, name=data is 3, user_data=slist
my_printf: id=4, name=data is 4, user_data=slist
my_printf: id=0, name=[new]data is 0, user_data=clist
my_printf: id=1, name=[new]data is 1, user_data=clist
my_printf: id=2, name=[new]data is 2, user_data=clist
my_printf: id=3, name=[new]data is 3, user_data=clist
my_printf: id=4, name=[new]data is 4, user_data=clist
OK
专题
查找失败
g_slist_position对待查找的节点有严格要求,如果不符合,就会查找失败。
待查找的节点,必须是链表中的节点。即待查找的节点,其内存地址与链表中节点的内存地址相同,因为查找对比的是地址而非值。
源码见glib_examples\glib_slist\g_slist_position
示例程序
#include <glib.h>
typedef struct mydata_tag {
int id;
char *name;
}my_data_t;
void my_struct_free_func(gpointer data)
{
my_data_t *mydata = NULL;
mydata = (my_data_t *)data;
if(NULL != mydata->name) {
//g_print("free mydata->name: %s \n", mydata->name);
free(mydata->name);
}
return;
}
void my_struct_printf(gpointer data, gpointer user_data)
{
g_print("%s: id=%d, name=%s, user_data=%s\n", __FUNCTION__,
((my_data_t *)data)->id, ((my_data_t *)data)->name, (char *)user_data);
}
static void test_slist_find_failed(void)
{
gint i = 0;
GSList *slist = NULL;
GSList *slink = NULL;
my_data_t *snode = NULL;
my_data_t mydata[16];
gint pos = 0;
my_data_t pos_node = {0, NULL};
GSList pos_link = {NULL, NULL};
char name[16] = {0};
memset(mydata, 0, sizeof(my_data_t)*5);
for(i=0;i<5;i++) {
mydata[i].id = i;
g_snprintf(name, 16, "data is %d", i);
mydata[i].name = strdup(name);
slist = g_slist_append(slist, (gpointer)&mydata[i]);
}
g_print("\n");
g_print("len=%d \n", g_slist_length(slist));
g_slist_foreach(slist, my_struct_printf, "my_struct_printf");
pos_node.id = 3;
pos_node.name = strdup("data is 3");
pos_link.data = &pos_node;
pos_link.next = NULL;
pos = g_slist_position(slist, &pos_link);
g_printf("pos=%d \n", pos);
if(pos<0) {
g_print("not found! \n");
}
free(pos_node.name);
g_slist_free_full(slist, my_struct_free_func);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/slist/find_failed", test_slist_find_failed);
return g_test_run ();
}
运行结果:
[root@centos7_6 build]# ./glib_slist_find_failed/glib_slist_find_failed
/slist/find_failed:
len=5
my_struct_printf: id=0, name=data is 0, user_data=my_struct_printf
my_struct_printf: id=1, name=data is 1, user_data=my_struct_printf
my_struct_printf: id=2, name=data is 2, user_data=my_struct_printf
my_struct_printf: id=3, name=data is 3, user_data=my_struct_printf
my_struct_printf: id=4, name=data is 4, user_data=my_struct_printf
pos=-1
not found!
OK
上面例子中,pos_link的所有成员的值和slist链表中的一个节点内容都一样,但它们并不是同一个节点,pos_link的内存在栈上,链表中id等于3的节点,内存在堆上(其内存是通过
g_slist_append分配出来的),两者只是值相同,并非同一个。
自定义一个值对比的查找函数
为了解决上面查找失败的问题,可以使用g_slist_find和g_slist_find_custom函数,或者自己写一个值对比的查找函数。下面的示例程序演示自定义值对比查找函数。
源码见glib_examples\glib_slist\glib_slist_find_custom_func
示例程序
#include <glib.h>
typedef struct mydata_tag {
int id;
char *name;
}my_data_t;
void my_struct_free_func(gpointer data)
{
my_data_t *mydata = NULL;
mydata = (my_data_t *)data;
if(NULL != mydata->name) {
//g_print("free mydata->name: %s \n", mydata->name);
free(mydata->name);
}
return;
}
void my_struct_printf(gpointer data, gpointer user_data)
{
g_print("%s: id=%d, name=%s, user_data=%s\n", __FUNCTION__,
((my_data_t *)data)->id, ((my_data_t *)data)->name, (char *)user_data);
}
static gint _slist_position_custom(GSList *list, GSList *llink)
{
gint i = 0;
while (list) {
if (((my_data_t *)(list->data))->id == ((my_data_t *)(llink->data))->id)
return i;
i++;
list = list->next;
}
return -1;
}
static void test_slist_find_custom_func(void)
{
gint i = 0;
GSList *slist = NULL;
GSList *slink = NULL;
my_data_t *snode = NULL;
my_data_t mydata[16];
gint pos = 0;
my_data_t pos_node = {0, NULL};
GSList pos_link = {NULL, NULL};
char name[16] = {0};
memset(mydata, 0, sizeof(my_data_t)*5);
for(i=0;i<5;i++) {
mydata[i].id = i;
g_snprintf(name, 16, "data is %d", i);
mydata[i].name = strdup(name);
slist = g_slist_append(slist, (gpointer)&mydata[i]);
}
g_print("\n");
g_print("len=%d \n", g_slist_length(slist));
g_slist_foreach(slist, my_struct_printf, "my_struct_printf");
pos_node.id = 3;
pos_node.name = strdup("data is 3");
pos_link.data = &pos_node;
pos_link.next = NULL;
pos = _slist_position_custom(slist, &pos_link);
g_print("pos=%d \n", pos);
free(pos_node.name);
g_slist_free_full(slist, my_struct_free_func);
}
gint main (gint argc, gchar *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/slist/find_custom_func", test_slist_find_custom_func);
return g_test_run ();
}
运行结果:
[root@centos7_6 build]# ./glib_slist_find_custom_func
/slist/find_custom_func:
len=5
my_struct_printf: id=0, name=data is 0, user_data=my_struct_printf
my_struct_printf: id=1, name=data is 1, user_data=my_struct_printf
my_struct_printf: id=2, name=data is 2, user_data=my_struct_printf
my_struct_printf: id=3, name=data is 3, user_data=my_struct_printf
my_struct_printf: id=4, name=data is 4, user_data=my_struct_printf
pos=3
OK