我们都知道glib使用gpointer这个类型来存储数据。这样做的好处就是可以存储多种类型的数据,比如存储int,float类型的数据,只需要将指针传进去即可。不过我在使用glib时却遇到了一些令人疑惑不解的东西。
由于glib使用gpointer来存储数据,而gpointer只是一个void类型的指针(typedef void* gpointer),所以存储时应该将数据的指针传递给函数。比如GList添加数据的时候用到了函数g_list_append,这个函数接收两个参数,一个是GList指针,一个就是gpointer变量。在glib的源代码当中,我们可以清晰的看到,这个函数只是将这个gpointer参数赋值给list的data变量。
GList*
g_list_append (GList *list,
gpointer data)
{
GList *new_list;
GList *last;
new_list = _g_list_alloc ();
new_list->data = data; //将指针赋值给new_list中的data变量
new_list->next = NULL;
if (list)
{
last = g_list_last (list);
/* g_assert (last != NULL); */
last->next = new_list;
new_list->prev = last;
return list;
}
else
{
new_list->prev = NULL;
return new_list;
}
}
这并不是说这样做不行,只是会有一个问题,那就是当添加的这个数据已经被回收了,那这个函数GList就包含了一项垃圾项了。
#include <stdio.h>
#include <stdlib.h>
#include <glib.h>
int main() {
GList* list = NULL;
gint *data = g_malloc(sizeof(gint));
*data = 1;
list = g_list_append(list, data);
//g_free(data);
void data_free(gpointer);
g_list_foreach(list, (GFunc)data_free, NULL);
g_list_free(list);
return 0;
}
void data_free(gpointer data){
if(NULL == data)
return ;
free(data);
}
上面这段代码可以正常执行,但是如果将g_free(data)这一行的注释符去掉,程序就会出现内存错误。由于GList在查找的时候是基于地址的查找,所以有时候即使是数据相等的两个数据,但是因为不是同一个数据,就不能算作相等。
gint
g_list_index (GList *list,
gconstpointer data)
{
gint i;
i = 0;
while (list)
{
if (list->data == data)
return i;
i++;
list = list->next;
}
return -1;
}
这个函数是GList 用于查找给定数据的位置的。从函数中我们可以看出,list只是简单地比较list->data和data而已。如果我有一个变量double d1 = 1.0,我将他添加到GList* list这个链表中去,其实我只是将&d1赋值给了list->data。如果我还有另外一个变量double d2 = 1.0,而我调用g_list_index这个函数,并将&d2传递过去,这样是找不到1.0这个数据的位置的。这一点在字符串操作上是很明显的:
#include <stdio.h>
#include <stdlib.h>
#include <glib.h>
int main() {
GList* list = NULL;
list = g_list_append(list, "one");
list = g_list_append(list, "two");
list = g_list_append(list, "three");
//gchar key[] = "two";
gchar* key = "two";
gint ind = g_list_index(list,key);
printf("the index of one is:%d\n",ind);
g_list_free(list);
return 0;
}
当key是指向一个静态变量时,我们可以得到“two”的位置。但是,当key是指向一个数组时,我们就无法得到位置了。
因为在调用g_list_append这个函数是,“two”代表的是一个静态变量,假设他的地址为0x123,当key指向一个静态变量是(gchar* key = "two"),key的地址也是0x123。
虽然GList也提供了一个一个函数g_list_find_costom可以自己定义数据之间比较的方法,但是还有有大量的函数只能进行地址比较。
上面提到的这两个问题只能通过一些比较麻烦的方法解决。比如对数据的释放,我们在实际编程过程中,需要注意,不用自行释放数据,要等到释放list的时候一起示范数据。对于list只能比较地址的问题,我们要注意维护好原始的数据和地址,并使用原始的数据地址来比较。
当然,glib还提供了很多其实的数据类型如hashtable,借鉴这些数据结构,我觉得GList可以改写成下面这种形式:
typedef struct _GList{
ListNode* head;
gint length;
GCompareFunc com_func;
GDestroyNotify destroy_func;
}GList;
typedef struct _ListNode{
gpointer data;
ListNode* prev;
ListNode* next;
}ListNode;
就像hashtable一样