glist

definition:
 
struct GList
{
gpointer data;
   GList 
*next;
   GList 
*prev;
};
 
/*
 * file: g_list.c
 * desc: 这个文件用于演示glib库里双向链表GList的用法
 * compile: gcc -o g_list g_list.c `pkg-config --cflags --libs glib-2.0`
 
*/

#include 
<glib.h>

void display_list(GList *list)
{
    GList 
*it = NULL;

    
/* 遍历链表 */
    
for (it = list; it; it = it->next) {
        printf(
"%s", it->data);
    }

    printf(
"/n");
}

int main(int argc, char *argv[])
{
    GList 
*list = NULL;

    
/*
     * 向链表尾部追加节点
     
*/
    list 
= g_list_append(list, "one ");
    list 
= g_list_append(list, "two ");
    list 
= g_list_append(list, "three ");
    list 
= g_list_append(list, "four ");
    list 
= g_list_append(list, "five ");
    display_list(list);

    
/* 在链表头部插入*/
    list 
= g_list_prepend(list, "zero ");
    display_list(list);

    GList 
*it = NULL;

    
/* 查找链表中的节点 */
    it 
= g_list_find(list, "two ");
    printf(
"Find data "two ": %s/n", it->data);

    
int index = 0;

    
/* 确定链表指针指向链表中的第几个节点 */
    index 
= g_list_position(list, it);
    printf(
"index of "two " is: %d/n", index);

    it 
= g_list_nth(list, 1);
    printf(
"%s/n", it->data);

    
/* 从链表里删除一个节点 */
    printf(
"After remove data "three ", the list:/n");
    list 
= g_list_remove(list, "three ");
    display_list(list);

    
/* 向链表里插入一个节点 */
    printf(
"After insert string "INSERT " at index 3, the list:/n");
    list 
= g_list_insert(list, "INSERT "3);
    display_list(list);

    
/* 采用内循环遍历链表节点 */
    g_list_foreach(list, (GFunc)printf, NULL);
    printf(
"/n");

    GList 
*last = NULL;

    
/* 取链表的最后一个节点 */
    last 
= g_list_last(list);

    
/* 从最后一个节点开始遍历链表 */
    
for (it = last; it; it = it->prev) {
        printf(
"%s", it->data);
    }
    printf(
"/n");

    
/* 销毁链表 */
    g_list_free(list);

    
return 0;
}
 
在linux下使用下面的命令编译:

gcc -o g_list g_list.c `pkg-config --cflags --libs glib-2.0`

编译后程序运行的结果如下:
$ ./g_list
one two three four five
zero one two three four five
Find data "two ": two
index of "two " is: 2
one
After remove data "three ", the list:
zero one two four five
After insert string "INSERT " at index 3, the list:
zero one two INSERT four five
zero one two INSERT four five
five four INSERT two one zero

现在你应该知道我为什么在每个字符串后面加一个空格了吧。

总结:在glib库中,双向链表的用法跟单向链表基本上是一致的,只是多了一个访问上一个节点的指针prev。对于你的应用如果单向链表够用,就没有必要使用双向链表,因为这里要增加维护一个指针的开销。
 
 
 
双向链表

概念

双向链表与单向链表非常类似,不过它们包含有另外的指针,以支持更多导航选项;给定双向链表中的一个节点,可以向前移动,也可以向后移动。这使得它们比单向链表更灵活,但也使用了更多内存,所以,除非确实需要这种灵活性,否则不要使用这种双向链表。

GLib 包含有一个名为 GList 的双向链表实现。GList 中的大部分操作与 GSList 中的类似。我们将查看基本用法的一些示例,然后是 GList 所支持的附加操作。

基本操作

这里是使用 GList 可以进行的一些常见操作:


//ex-glist-1.c
#include <glib.h>
int main(int argc, char** argv) {
GList* list = NULL;
list = g_list_append(list, "Austin ");
printf("The first item is '%s'\n", list->data);
list = g_list_insert(list, "Baltimore ", 1);
printf("The second item is '%s'\n", g_list_next(list)->data);
list = g_list_remove(list, "Baltimore ");
printf("After removal of 'Baltimore', the list length is %d\n", g_list_length(list));
GList* other_list = g_list_append(NULL, "Baltimore ");
list = g_list_concat(list, other_list);
printf("After concatenation: ");
g_list_foreach(list, (GFunc)printf, NULL);
list = g_list_reverse(list);
printf("\nAfter reversal: ");
g_list_foreach(list, (GFunc)printf, NULL);
g_list_free(list);
return 0;
}

***** Output *****

The first item is 'Austin '
The second item is 'Baltimore '
After removal of 'Baltimore', the list length is 1
After concatenation: Austin Baltimore
After reversal: Baltimore Austin


上面的代码看起来非常熟悉!上面所有操作都在 GSList 中出现过;唯一的区别是 GList 的函数名是 g_list 开头,而不是 g_slist。当然,它们全都要使用指向 GList 结构体而不是指向 GSList 结构体。




更好的导航

已经了解了一些基本的 GList 操作后,这里是一些可能的操作,唯一的原因就是 GList 中的每个节点都有一个指向前一个节点的链接:


//ex-glist-2.c
#include <glib.h>
int main(int argc, char** argv) {
GList* list = g_list_append(NULL, "Austin ");
list = g_list_append(list, "Bowie ");
list = g_list_append(list, "Charleston ");
printf("Here's the list: ");
g_list_foreach(list, (GFunc)printf, NULL);
GList* last = g_list_last(list);
printf("\nThe first item (using g_list_first) is '%s'\n", g_list_first(last)->data);
printf("The next-to-last item is '%s'\n", g_list_previous(last)->data);
printf("The next-to-last item is '%s'\n", g_list_nth_prev(last, 1)->data);
g_list_free(list);
return 0;
}

***** Output *****

Here's the list: Austin Bowie Charleston
The first item (using g_list_first) is 'Austin '
The next-to-last item is 'Bowie '
The next-to-last item is 'Bowie '


没有太令人惊讶的事情,不过有一些注解:

* g_list_nth_prev 的第二个参数是一个整数,表明需要向前导航多少个节点。如果传递的值超出了 GList 的范围,那么要准备好处理程序的崩溃。
* g_list_first 是一个 O(n) 操作。它从一个给定的节点开始向后搜索,直到找到 GList 的头。上面的示例是一个最坏的情形,因为遍历是从列表的末尾开始的。同理,g_list_last 也是一个 O(n) 操作。





使用链接删除节点

已经了解了在拥有指向其容纳的数据的指针的前提下如何从列表中删除一个节点;g_list_remove 可以很好地完成。如果拥有一个指向节点本身的指针,可以通过一个快速的 O(1) 操作直接删除那个节点。


//ex-glist-3.c
#include <glib.h>
int main(int argc, char** argv) {
GList* list = g_list_append(NULL, "Austin ");
list = g_list_append(list, "Bowie ");
list = g_list_append(list, "Chicago ");
printf("Here's the list: ");
g_list_foreach(list, (GFunc)printf, NULL);
GList* bowie = g_list_nth(list, 1);
list = g_list_remove_link(list, bowie);
g_list_free_1(bowie);
printf("\nHere's the list after the remove_link call: ");
g_list_foreach(list, (GFunc)printf, NULL);
list = g_list_delete_link(list, g_list_nth(list, 1));
printf("\nHere's the list after the delete_link call: ");
g_list_foreach(list, (GFunc)printf, NULL);
g_list_free(list);
return 0;
}

***** Output *****

Here's the list: Austin Bowie Chicago
Here's the list after the remove_link call: Austin Chicago
Here's the list after the delete_link call: Austin


所以如果拥有一个指向节点而不是数据的指针,那么可以使用 g_list_remove_link 删除那个节点。

删除它以后,还需要使用 g_list_free_1 显式地释放它,其所做的事情正如名字所暗示的:它释放一个节点。照例,需要保存好 g_list_remove_link 的返回值,因为那里是列表的新起始位置。

最后,如果想做的只是删除并释放某个节点,那么可以调用 g_list_delete_link 来一步完成。

GSList 也有相同的函数;只需要将 g_list 替换为 g_slist,上面的所有信息都适用。




索引和位置

如果只是想找出某个条目在 GList 中的位置,那么有两种选择。可以使用 g_list_index,它可以使用条目中的数据找出它,或者可以使用 g_list_position,它使用的是指向那个节点的指针。这个示例展示了这两种方法:


//ex-glist-4.c
#include <glib.h>
int main(int argc, char** argv) {
GList* list = g_list_append(NULL, "Austin ");
list = g_list_append(list, "Bowie ");
list = g_list_append(list, "Bowie ");
list = g_list_append(list, "Cheyenne ");
printf("Here's the list: ");
g_list_foreach(list, (GFunc)printf, NULL);
printf("\nItem 'Bowie' is located at index %d\n", g_list_index(list, "Bowie "));
printf("Item 'Dallas' is located at index %d\n", g_list_index(list, "Dallas"));
GList* last = g_list_last(list);
printf("Item 'Cheyenne' is located at index %d\n", g_list_position(list, last));
g_list_free(list);
return 0;
}

***** Output *****

Here's the list: Austin Bowie Bowie Cheyenne
Item 'Bowie' is located at index 1
Item 'Dallas' is located at index -1
Item 'Cheyenne' is located at index 3


注意,如果 g_list_index 找不到那个数据,则它返回的值为 -1。如果两个节点具有相同的数据值,那么 g_list_index 会返回最先出现的索引。如果找不到指定的节点, g_list_position 也会返回 -1。

另外,在 GSList 也有这些方法,只是名字不同。




实际应用

让我们来研究在前面提到的开放源代码的应用程序中 GList 的应用。

Gaim 使用了很多 GList:

* 在关闭“add a buddy”对话框时,gaim-1.2.1/plugins/gevolution/add_buddy_dialog.c 使用 g_list_foreach 调用来释放对每个联系人的引用。
* gaim-1.2.1/src/account.c 使用一个 GList 来保存所有帐号;它使用 g_list_find 来确保某个帐号在使用 g_list_append 进行添加之前并不存在。

Evolution 使用 GList:

* evolution-2.0.2/filter/filter-rule.c 使用 GList 来保存邮件过滤规则的各部分(比如要检查的主题行); filter_rule_finalise 使用 g_list_foreach 释放对那些部分的引用。
* evolution-2.0.2/calendar/gui/alarm-notify/alarm.c 使用 GList 保存警告; queue_alarm 借助一个定制的 GCompareFunc 使用 g_list_insert_sorted 将新的警告插入到适当的位置。

在 GIMP 中的应用:

* gimp-2.2.4/app/file/gimprecentlist.c 使用 GList 保存最近访问过的文件; gimp_recent_list_read 从某个 XML 文件描述符中读取文件名,并在返回 GList 之前调用 g_list_reverse。
* gimp-2.2.4/app/vectors/gimpbezierstroke.c 使用 GList 保存笔划连接点(stroke anchors); gimp_bezier_stroke_connect_stroke 使用 g_list_concat 来帮助将一个笔划连接到另一个笔划。
posted @ 2011-11-23 14:50  alxe_yu  阅读(894)  评论(0编辑  收藏  举报