4_06_GLib库入门与实践_单链表

简介

单向链表是一种常见的数据结构,它只有一个后向指针,结构简单。

数据结构

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
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值