如何使用Glib工具集管理C数据_第1页
如何使用Glib工具集管理C数据_第2页
如何使用Glib工具集管理C数据_第3页
如何使用Glib工具集管理C数据_第4页
如何使用Glib工具集管理C数据_第5页
已阅读5页,还剩75页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、如何使用GLib工具集管理C数据不是一个学院派的东西,也不是凭空想出来的,完全是在开发的过程中,慢慢总结和完善的结果。如果你是一个工作3年以上的C语言程序员,现在让你讲讲写程序的苦恼,你可能有很多话要说,但如果你有时间研究一下,你会发现,很多苦恼已不再成其为苦恼,里很多东西正是你期望已经久的。oje是的精粹,是用C实现的,但在很大程序是基于面向对象思想设计的,oje是所有类的基类。sr在其中也是一大特色,s与操作系统中的s并不一样,它是类似消息一样的东西,让消息在各个对象间传递,但尽量降低对象间的耦合。仔细读一下它的代码,唯一想说的话就是“绝!”。动态数组、链表、哈希表等通用容器,在不同的公司

2、,在不同的时期,在不同的情况下,我们每个人对每一种容器,可能都实现过N次以上。甚至在同一个项目里,出现几份链表的实现,也并非罕见。一直在抱怨,标准C中为什么没有类似于STL的标准容器,让全世界的程序员在数以万次的重复实现它们。不过,还算走运,有了,恶梦在此终结了。提供了动态数组、单/双向链表、哈希表、多叉树、平衡二叉树、字符串等常用容器,完全是面向对象设计的,实现得非常精致。不用白不用,别客气了。组织数据GL的范畴首先研究GL的范畴。GL是一个提供了很多实用定义和函数的底层程序库,包括基本类型及其限定的定义、标准宏、类型转化、字节次序、内存分配、警告与断言、消息日志、计时器、字符工具、钩子函数

3、、词法扫描器、模块的动态加载,以及自动的字符串补齐。GL还定义了很多数据结构(以及它们相关的操作),包括:内存块(Memorychunks双向链表(Doubly-linkedlis)s单向链表(Singly-linkedlis)s散列表(Hashtables字符串(Strings,可以动态增长)字符块(Stringchunks成组的字符串)数组(Arrays,当增加元素时其大小能够增长)平衡二叉树(Balancedbinarytr)esN-叉树(N-arytre)sQuarks(字符串和唯一整型标识符的双向关联)有关键字的数据列表(Keyeddata,st通过字符串或者整型id访问其数据元素的

4、列表)关系(Relations)和元组(tuples)(可以由任意数目的域进行索引的数据表)缓存组织数据GLib的范畴首先研究GLib的范畴。GLib是一个提供了很多实用定义和函数的底层程序库,包括基本类型及其限定的定义、标准宏、类型转化、字节次序、内存分配、警告与断言、消息日志计时器、字符工具、钩子函数、词法扫描器、模块的动态加载,以及自动的字符串补齐。GLib还定义了很多数据结构(以及它们相关的操作),包括:内存块(Memorychunks双向链表(Doubly-linkedlis)s单向链表(Singly-linkedlis)s散列表(Hashtables字符串(Strings,可以动态

5、增长)字符块(Stringchunks成组的字符串)数组(Arrays,当增加元素时其大小能够增长)平衡二叉树(BalancedbinarytrkesN-叉树(N-arytreksQuarks(字符串和唯一整型标识符的双向关联)有关键字的数据列表(Keyeddata,st通过字符串或者整型id访问其数据元素的列表)关系(Relations)和元组(tuples)(可以由任意数目的域进行索引的数据表)缓存每个程序都必须管理数据编写程序是为了处理数据。程序可能会从文件中读出一个名字列表、通过一个图形用户界面向用户询问某些数据,或者从一个外部的硬件设备加载数据。但数据一旦到了程序中,就需要保持对它的

6、追踪。用来管理数据的函数和变量称为数据结构或容器。如果使用C编写代码,那么您会发现它极其缺乏复杂的数据结构。当然,有很多存储数据的简单方法:基本类型in、tfloat、char等等。枚举(enum),可以保存整数的一系列符号名称。数组(array),这是C中最灵活的数据结构。数组可以保存基本类型的数据,或者一系列任意类型的数据,或者指向任意类型数据的指针。但是数组也有很多局限性。它们的大小不能改变,所以,如果为一个拥有十个元素的数组分配了内存,却发现需要向其中放置十一个元素,那么就得创建一个新数组,将旧的元素拷贝过来,然后添加并新的元素。如果要遍历数组中的每一个元素,那么,或者需要始终知道数组

7、中有多少个元素,或者要确保在数组的末尾处有某种“数组结束”标记,以使得您能够知道何时停止。通过使用链表和二叉树等标准容器,在C中保持对数据的追踪的问题已经多次得到解决。每一位刚开始学习计算机科学专业的学生都会使用一个数据结构类;教师肯定会布置一系列编写那些容器的实现的练习。在编写这些数据结构时,学生会意识到它们是多么难以处理;粗心的学生经常会犯诸如悬空指针(danglingpointer和重复释放内存(free)的错误。编写单元测试会有很大帮助,但是,总的来说,为每个新程序重新编写相同的数据结构是一个费力不讨好的任务。内置的数据结构简言之就是,在什么地方内置数据结构会有所帮助。有一些语言会内置

8、附带这些容器。+包含标准模板库(StandardTemplateLibraifyTL),它有一个容器类工具集,比如列表、优先队列、集合(set)和映射。另外,这些容器是类型-安全(type-safe)的,也就是说,您只能向创建的每一个容器对象中放入一种类型的元素。这就使得它们的使用更为安全,并避免了C所要求的很多冗长的强制类型转换。并且,STL包含了很多遍历工具和排序工具,这样就使得容器的使用更为简单。Java编程环境也附带了一组容器类。Thejava.ut程序包包含ArrayList、HashMap、TreeSet以及其他各种标准结构。它还包括有用于对数据进行一般排序、创建不变集合的工具,以

9、及其他各种便利工具。不过,C并没有内置容器支持;您或者只能自己编写,或者使用其他人的数据结构程序库。幸运的是,GLib是一个能够满足此需要的优秀的、免费的、开放源代码的程序库。它包含了大部分标准数据结构,以及在程序中有效处理数据的很多实用工具。它诞生于1996年,从而经过了彻底地测试,并在发展过程中增加了很多实用功能。100字以内(或者更少)的算法分析对容器的不同操作需要不同长度的时间。例如,在一个长的列表中,访问第一个元素要快于对同一列表进行排序。用来描述完成这些操作所需时间的符号称为O-notation。这个话题需要计算机科学专业的学生用一个学期的时间去研究,但是,简要地讲,O-notat

10、ion是对操作的最坏情况的分析。换句话说,它对完成某个操作所需要的最长时间进行度量。它被证明是度量数据结构操作的有效途径,因为经常会遇到最坏情况的操作(比如当您搜索某个列表而却没有找到所找查找的条目时)。接下来论述了一些O-notation示例,可以将一副扑克牌在桌子上排列一行:O(1)一-择第一张牌。O(1)也称为“线性时间(constanttim)”,因为不管在列表中有多少张牌,取得第一张牌的时间都是相同的。O(n)羽转每一张牌0O(n)被称为“线性时间(lineartim)”,因为随着牌的数目增加,完成操作所需时间线性增加。O(n!)创建一个全部扑克牌所有可能排列的列表。每向列表添加一张

11、新牌,排列的数目都会阶乘式增加。您会发现,在整个教程都提及关于不同数据结构操作的O-notation。了解特定数据结构上特定操作的开销,能够帮助您更明智地选择容器在,最优化应用程序的性能。编译GLib程序如果在研究那些示例时编译并运行它们,那么您将通过本文学到更多。由于它们要使用GLib所以编译器需要知道GLib头文件和程序库在哪里,以使其能够解析GLib定义的类型。这个简单的程序初始化一个双向链表并向其中添加一个字符串:/ex-compile.c#includeintmain(intargc,char*argv)GList*list=NULL;list=g_list_append(list,

12、Helloworld!);printf(Thefirstitemis%sn,g_list_first(list)-data);return0;可以通过调用GCC来编译这个程序,如下:$gcc-I/usr/include/glib-2.0-I/usr/lib/glib-2.0/inclu-ldgelib-2.0-oex-compileex-compile.c运行它,查看期望的输出:$./ex-compileThefirstitemisHelloworld!$不过,那是一个非常难用的GCC调用。更简单的方式是让GCC指向GLib程序库。使用pkg-config手工指定程序库的位置容易出错而且很冗长

13、,所以大部分现代的Linux发行版本都附带了pkgconfig工具来帮助简化此问题。可以使用pkgconfig来编译上面的程序,如下:$gccpkg-config-cflags-libsglib-2.0-oex-compileex-compile.c输出与先前相同:$./ex-compileThefirstitemisHelloworld!$注意,现在不必再指定GLib头文件的路径;pkgconfig的-cflags选项会完成此任务。使用-libs选项指定的程序库也是如此。当然,没有任何神密之处;pkgconfig只是通过一个配置文件读取出程序库和头文件的位置。在FedoraCore3系统中,

14、pkgconfig文件位于/usr/lib/pkgconfig目录下,glib-2.0.pc文件类似如下:$cat/usr/lib/pkgconfig/glib-2.0.pcprefix=/usrexec_prefix=/usrlibdir=/usr/libincludedir=/usr/includeglib_genmarshal=glib-genmarshalgobject_query=gobject-queryglib_mkenums=glib-mkenumsName:GLibDescription:CUtilityLibraryVersion:2.4.7Libs:-L$libdir-l

15、glib-2.0Cflags:-I$includedir/glib-2.0-I$libdir/glib-2.0/include这样,所有信息都由一个中间层隐藏起来。如果恰巧使用了一个不支持pkgconfig的Linux发行版本,那么随时可以重新直接为GCC指定头文件和程序库。GLib的现实应用仅仅列举出GLib容器并展示示例用法可能还不够,所以本教程还包含了GLib在一些开放源代码的应用程序中的现实应用:Gaim是一款流行的实时消息客户机,它在SourceForge上的下载次数每个月都会超过二十五次。GIMP(GraphicalImageManipulationPro)是mGLib本身的出发点

16、(startingpoi)它是一款广为应用的图像处理程序,从1996年开始一直得到公众的共同开发。Evolution是一款极好的个人信息管理器(PIM),可以追踪电子邮件、联系人、任务和约会。研究GLib在这些流行的应用程序中的应用还会让您有机会了解一些编码习惯;不仅能知道函数的名字是什么,您还能够了解通常如何使用它们。您将会熟悉将要使用的容器,而且甚至会注意到在某些情况下有人选择了并不最适于某任务的容器。GLib还拥有很多约定(conventions)及工具宏。在学习本教程的过程中,您将发现使用并解释了其中的很多。不必尝试一开始就将它们完全记住,而只需要在进行过程中去学习它们,在示例中去了解

17、它们。单向链表概念或许在GLib中最简单的容器就是单向链表;即GSList。如其名称所示,它是一系列链接到一起的数据条目,可以从一个数据条目导航到下一个数据条目。它之所以称为单向链表,是因为在条目之间只有一个单向的链接。所以,只能“向前”遍历列表,但不能向前移动后再向后退。进一步讲,每次向列表添加一个条目时,都会创建一个新的GSList结构体。这个GSList结构体由一个数据条目和一个指针构成。先前列表的末尾指向这个新的节点,这表示现在这个新节点在列表的末尾。这里的术语可能令人费解,因为整个结构体称为GSList,而每个节点也是一个GSList结构体。不过,概念上讲,一个列表只是多个列表的一个

18、序列,其中每个列表有一个条目。这就像是在红灯前的一行汽车;就算是只有一辆汽车在红灯前等待,它也被认为是一行汽车。将一长串条目链接起来,随之而来有一些用法。确定列表的长度是一个O(n)操作;因为只有统计了每一个条目才能指出列表有多长。向列表的起始端添加条目很快(一个O(1)操作),因为列表的长度不是固定的,超出限制值后不必重新构建。不过,查找某个条目是一个O(n)操作,因为需要对整个列表进行一次线性搜索,直到找到所有寻找的条目。向列表的末尾添加一个条目也是一个O(n)操作,因为要想到达末尾,需要从头开始遍历直到列表的末尾。GSList可以保存两种基本类型的数据:整型或者指针。不过,这实际上意味着

19、几乎可以向GSList中放入任何内容。例如,如果需要一个“short”数据类型的GSList,那么只需要在GSList中放入一个指向short类型数据的指针。现在理论已经足够了;接下来真正地使用GSList!创建、添加和销毁下面的代码将初始化一个GSList,向其添加两个条目,打印出列表的长度,然后释放它:/ex-gslist-1.c#includeintmain(intargc,char*argv)GSList*list=NULL;printf(Thelistisnow%ditemslongn,g_slist_length(list);list=g_slist_append(list,fir

20、st);list=g_slist_append(list,second);printf(Thelistisnow%ditemslongn,g_slist_length(list);g_slist_free(list);return0;*Output*Thelistisnow0itemslongThelistisnow2itemslong关于以上代码的一些注解:大部分GLib函数的格式都是g_(containername)_(functionname)。所以,要得到某个GSList的长度,可以调用g_slist_length。没有创建新的GSList的函数;而只需要声明一个指向GSList结构体

21、的指针并分配一个NULL值。g_slist_append会返回列表的新起始地址,所以一定要保存那个返回值。g_slist_free不考虑列表中是否存放了条目;快速查看源代码可以发现,如果GSList为NULL,贝Ig_slist_free只是立即返回。g_slist_length也可以作用于空的列表;在那种情况下,它只是返回0。添加然后删除数据可以添加数据;可能还会需要将其删除。这里是一个示例:/ex-gslist-2.c#includeintmain(intargc,char*argv)GSList*list=NULL;list=g_slist_append(list,second);lis

22、t=g_slist_prepend(list,first);printf(Thelistisnow%ditemslongn,g_slist_length(list);list=g_slist_remove(list,first);printf(Thelistisnow%ditemslongn,g_slist_length(list);g_slist_free(list);return0;这段代码中大部分看起来都比较熟悉,不过有一些地方需要考虑;如果调用g_slist_remove时传递了一个并不在列表中的条目,那么列表将不会发生变化。g_slist_remove也会返回列表的新起始位置。可以发

23、现,first”是调用g_slist_prepend添加的。这是个调用比g_slist_append更快;它是O(1)操作而不是O(n)操作,原因如前所述,进行附加需要遍历整个列表。所以,如果使用g_slist_prepend更为方便,那么就应该使用它。删除重复的条目这里是当在一个列表中有重复的条目时会发生的问题:/ex-gslist-3.c#includeintmain(intargc,char*argv)GSList*list=NULL;list=g_slist_append(list,first);list=g_slist_append(list,second);list=g_slist

24、_append(list,second);list=g_slist_append(list,third);list=g_slist_append(list,third);printf(Thelistisnow%ditemslongn,g_slist_length(list);list=g_slist_remove(list,second);list=g_slist_remove_all(list,third);printf(Thelistisnow%ditemslongn,g_slist_length(list);g_slist_free(list);return0;OutputThelist

25、isnow5itemslongThelistisnow2itemslong所以,如果GSList包含了两个同样的指针,而调用了g_slist_remove,那么只会删除第一个指针。不过,可以使用g_slist_remove_all删除条目的所有指针。最后一个、第n个和第n个数据在GSList中有了一些条目后,可以通过不同的方式提取它们。这里是一些示例,并在printf语句中给出了解释。/ex-gslist-4.c#includeintmain(intargc,char*argv)GSList*list=NULL;list=g_slist_append(list,first);list=g_sl

26、ist_append(list,second);list=g_slist_append(list,third);printf(Thelastitemis%sn,g_slist_last(list)-data);printf(Theitematindex1is%sn,g_slist_nth(list,1)-data);printf(Nowtheitematindex1theeasyway:%sn,g_slist_nth_data(list,1printf(Andthenextitemafterfirstitemis%sn,g_slist_next(list)-data);g_slist_free

27、(list);return0;OutputThelastitemisthirdTheitematindex1issecondNowtheitematindex1theeasyway:secondAndthenextitemafterfirstitemissecond注意,有一些可以作用于GSList的快捷函数;可以简单地调用gslistnthdat而不需调用先gslistnt然后再反引用(dereference)返回的指针。最后一个printf语句稍有不同。gslistnex不是一个函数调用,而是一个宏。它展开为一个指向GSList中下一元素的指针反引用。在这种情况下,可以看到,我们传递了GS

28、List中的第一个元素,于是那个宏展并给出第二个元素。这也是一个快速的操作,因为没有函数调用的开销。退一步:使用用户定义的类型到现在为止我们一直在使用字符串;也就是说,我们只是将指向字符的指针放入到GSList中。在下面的代码示例中,将会定义一个Person结构体,并将这个结构体的一些实例放入到GSList中:/ex-gslist-5.c#includetypedefstructchar*name;intshoe_size;Person;intmain(intargc,char*argv)GSList*list=NULL;Person*tom=(Person*)malloc(sizeof(Pe

29、rson);tom-name=Tom;tom-shoe_size=12;list=g_slist_append(list,tom);Person*fred=g_new(Person,1);/allocatememoryforonePersonstructfred-name=Fred;fred-shoe_size=11;list=g_slist_append(list,fred);printf(Tomsshoesizeis%dn,(Person*)list-data)-shoe_size);printf(ThelastPersonsnameis%sn,(Person*)g_slist_last(

30、list)-data)-name);g_slist_free(list);free(tom);g_free(fred);return0;Output*Tomsshoesizeis12ThelastPersonsnameisFred关于使用GLib和用户定义类型的一些注解:可以像使用字符串一样在GSList使用用户定义类型。另外要注意,当从列表中取出条目时,需要进行一些强制类型转换。这个示例使用了另一个GLib宏g_new宏来创建FredPerson实例。这个宏只是展开并使用malloc为给定的类型分配适当数量的内存,但是这比手工输入malloc函数调用更为简洁。最后,如果要分配内存,那么还需要

31、释放它。可以看到上面的代码示例如何使用GLib函数g_free来为FredPerson实例完成此任务(因为它是使用g_new分配的)。在大部分情况下g_free只是会包装free函数,但是GLib也具备内存池功能,g_free以及其他内存管理函数可以使用它们。组合、反转,等等GSList附带了一些便利的工具,可以连接和反转列表。这里是它们的工作方式:/ex-gslist-6.c#includeintmain(intargc,char*argv)GSList*list1=NULL;list1=g_slist_append(list1,first);list1=g_slist_append(lis

32、t1,second);GSList*list2=NULL;list2=g_slist_append(list2,third);list2=g_slist_append(list2,fourth);GSList*both=g_slist_concat(list1,list2);rintf(Thethirditemintheconcatenatedlistis%sn,g_slist_nth_data(both,2);GSList*reversed=g_slist_reverse(both);printf(Thefirstiteminthereversedlistis%sn,reversed-dat

33、a);g_slist_free(reversed);return0;*Output*ThethirditemintheconcatenatedlististhirdThefirstiteminthereversedlistisfourth正如所预期的,两个列表首尾相连在一起,Iist2中的第一个条目成为新的列表中的第三个条目。注意,并没有拷贝条目;它们只是被链接上,这样内存只需要释放一次。另外,您会发现您只需要使用一个指针反引用(reersed-data就可以打印出反向列表的第一个条目。由于GSList中的每一个条目都是一个指向某个GSList结构体的指针,所以要获得第一个条目并不需要调用函数

34、。简单遍历这里是遍历GSList中所有内容的一个直观方法:/ex-gslist-7.c#includeintmain(intargc,char*argv)GSList*list=NULL,*iterator=NULL;list=g_slist_append(list,first);list=g_slist_append(list,second);list=g_slist_append(list,third);for(iterator=list;iterator;iterator=iterator-next)rintf(Currentitemis%sn,iterator-data);g_slis

35、t_free(list);return0;*Output*CurrentitemisfirstCurrentitemissecondCurrentitemisthird迭代器(iterator)对象只是一个声明为指向GSList结构体的变量。这看似奇怪,不过却能满足要求。由于单向列表是一系列GSList结构体,所以迭代器与列表的类型应该相同。另外,注意这个代码段使用的是通常的GLib用法习惯;在声明GSList本身的时候就声明了迭代器变量。最后,for循环的退出表达式检查迭代器是否为NULL。这样是有效的,因为只有当循环传递了列表中的最后一个条目后它才会成为NULL。使用函数进行高级遍历遍历列

36、表的另一种方法是使用g_slist_foreach,并提供一个将为列表中的每一个条目调用的函数。/ex-gslist-8.c#includevoidprint_iterator(gpointeritem,gpointerprefix)printf(%s%sn,prefix,item);voidprint_iterator_short(gpointeritem)printf(%sn,item);intmain(intargc,char*argv)GSList*list=g_slist_append(NULL,g_strdup(first);list=g_slist_append(list,g_s

37、trdup(second);list=g_slist_append(list,g_strdup(third);printf(Iteratingwithafunction:n);g_slist_foreach(list,print_iterator,-);printf(Iteratingwithashorterfunction:n);g_slist_foreach(list,(GFunc)print_iterator_short,NULL);printf(Nowfreeingeachitemn);g_slist_foreach(list,(GFunc)g_free,NULL);g_slist_f

38、ree(list);return0;*Output*Iteratingwithafunction:-first-second-thirdIteratingwithashorterfunction:firstsecondthirdNowfreeingeachitem在这个示例中有很多好东西:一条类似GSListxg_slist_append(NULL,wh的语句让您能够一举声明、初始化并将第一个条目添加到列表。g_strdup函数可以方便地复制字符串;如果使用了它,那么要记得释放。g_slist_foreach允许传递一个指针,这样就可以根据列表中的每个条目有效地为其赋与任意参数。例如,可以传递

39、一个累加器并收集关于列表中每个条目的信息。遍历函数的唯一受限之处在于它至少要使用一个gpointer作为参数;现在可以了解在只接收一个参数时print_interator_short如何工作。注意,代码使用一个内置的GLib函数作为g_slist_foreach的参数来释放所有字符串。在此示例中,所有需要做的只是将g_free强制类型转换为GFunc以使其生效。注意,仍然可以单独使用g_slist_free来释放GSList本身。使用GCompareFunc排序可以通过提供一个知道如何比较列表中条目的函数来对GSLit进行排序。下面的示例展示了对字符串列表进行排序的一种方法:/ex-gslis

40、t-9.c#includegintmy_comparator(gconstpointeritem1,gconstpointeritem2)returng_ascii_strcasecmp(item1,item2);intmain(intargc,char*argv)GSList*list=g_slist_append(NULL,Chicago);list=g_slist_append(list,Boston);list=g_slist_append(list,Albany);list=g_slist_sort(list,(GCompareFunc)my_comparator);printf(T

41、hefirstitemisnow%sn,list-data);printf(Thelastitemisnow%sn,g_slist_last(list)-data);g_slist_free(list);return0;OutputThefirstitemisnowAlbanyThelastitemisnowChicago注意,如果第一个条目小于第二个,则GCompareFunc返回一个负值,如果相等则返回0,如果第二个大于第一个则返回一个正值。只要您的比较函数符合此规范,在内部它可以做任何所需要的事情。另外,由于各种其他GLib函数也遵循此模式,所以可以简单地委派给它们。实际上,在上面的示例

42、中,可以简单地把my_comparator调用替换为g_slist_sort(list,(GComareFunc)g_ascii_strcasecm等调用,会获得相同的结果。有一些方法可以在GSList查找元素。已经介绍了如何简单地遍历列表的全部内容,比较每个条目,直到找到了目标条目。如果已经拥有了要寻找的数据,而只是想要获得它在列表中的位置,那么可以使用g_slist_find。最后,可以使用g_slist_find_custom,它允许您使用一个函数来检查列表中的每一个条目。下面展示了g_slist_find和g_slist_find_custom:/ex-gslist-10.c#incl

43、udegintmy_finder(gconstpointeritem)returng_ascii_strcasecmp(item,second);intmain(intargc,char*argv)GSList*list=g_slist_append(NULL,first);list=g_slist_append(list,second);list=g_slist_append(list,third);GSList*item=g_slist_find(list,second);printf(Thisshouldbetheseconditem:%sn,item-data);item=g_slis

44、t_find_custom(list,NULL,(GCompareFunc)my_finder);printf(Again,thisshouldbetheseconditem:%sn,item-data);item=g_slist_find(list,delta);(null);printf(deltaisnotinthelist,soweget:%sn,item?item-data:g_slist_free(list);return0;*Output*Thisshouldbetheseconditem:secondAgain,thisshouldbetheseconditem:secondd

45、eltaisnotinthelist,soweget:(null)注意,g_slist_find_custom也使用了一个可以指向任意内容的指针作为第二个参数,所以,如果需要,可以传递进来一些内容以帮助查找函数。另外,GCompare函数是最后一个参数,而不是第二个参数,因为它在g_slist_sort之中。最后,失败的查找会返回NULL。通过插入进行高级添加既然已经接触过几次GCompareFunc,一些更有趣的插入操作会更有意义。使用g_slist_insert可以将条目插入到指定的位置,使用g_slist_insert_before可以将条目插入到特定位置之前,使用g_slist_ins

46、ert_sorted可以进行有序插入。这里是样例:/ex-gslist-11.c#includeintmain(intargc,char*argv)GSList*list=g_slist_append(NULL,Anaheim),*iterator=NULL;list=g_slist_append(list,Elkton);printf(BeforeinsertingBoston,seconditemis:%sn,g_slist_nth(list,1)-data);g_slist_insert(list,Boston,1);printf(Afterinsertion,seconditemis:

47、%sn,g_slist_nth(list,1)-data);list=g_slist_insert_before(list,g_slist_nth(list,2),Chicago);printf(Afteraninsert_before,thirditemis:%sn,g_slist_nth(list,2)-data);list=g_slist_insert_sorted(list,Denver,(GCompareFunc)g_ascii_strcasecmp);printf(AfterinsertingDenver,heresthefinallist:n);g_slist_foreach(l

48、ist,(GFunc)printf,NULL);g_slist_free(list);return0;OutputBeforeinsertingBoston,seconditemis:ElktonAfterinsertion,seconditemis:BostonAfteraninsert_before,thirditemis:ChicagoAfterinsertingDenver,heresthefinallist:AnaheimBostonChicagoDenverElkton由于g_slist_insert_sorted使用了GCompareFunc,所以复用内置的GLib函数g_asc

49、ii_strcasecmp也很简单。现在您应该已经能理解为什么在每个条目的末尾有额外的空间;在代码示例的最后加入了另一个g_slist_foreach示例,这一次是使用GFunc作为参数。实际应用在先前提到的三个开放源代码的实际应用程序中,可以发现在很多地方使用了GSList。大部分用法相当普通,有很多插入、附加、删除,等等。不过有一些更有趣的东西。Gaim使用GSList来保存当前会话以及大部分插件中的各种内容:gaim-1.2.1/src/away.c使用有序的GSList来保存“离开消息”(客户机离线期间收到的消息)。它使用了一个定制的GCompareFunc,即sort_aaymsg_

50、list用于保存那些以发送者名称排序的消息。gaim-1.2.1/src/protocols/gg/gg.c使用GSList来保存允许帐号的一个列表;然后它使用一个定制的查找器来确认某个帐号在列表中。定制的查找器简单地委派g_ascii_strcasecmp,所以有可能会弃用它,并将g_ascii_strcasecmp直接传递给g_slist_find_custom。Eolution同样使用了很多GSList:eolution-data-serer-1.9.2/calendar/libecal/e-cal-component使用GSList来存会议参与者。有时,它会反复调用g_slist_pr

51、epend,并在结束时使用g_slist_reerse令条目按期望排序,以此构建那个GSList。如前所述,这样做比使用g_slist_append添加条目更快。eolution-2.9.2/addressbook/gui/contact-editor/e-contact-editor.c在一种卫述句(guardclaus)中使用g_slist_find;它在信号处理器中使用它,以确保它在一个信号回调中接收到的EContactEditor在被作为参数传递到某个函数之前能够保存。GIMP也以一些适宜的方式使用了GSList:gimp-2.2.4/plug-ins/maze/algorithms.

52、c在迷宫生成(maze-generations)算法中使用GSList追踪单元(cells)。gimp-2.2.4/app/widgets/gimpclipboard.c使用GSList来保存剪贴板象素缓冲区格式(比如PNG和JPEG);它向g_slist_sort传递一个定制的GCompareFunc。gimp-2.2.4/app/core/gimppreiecache.使用GSList作为一种基于大小的队列;它在GSList中保存图象预览,使用g_slist_insert_sorted优先插入较小的图象。同一文件中的另一个函数会遍历同一个GSList并比较每一个条目的表面区域,找出要删除的

53、最小那一个,以此来整理缓存。双向链表概念双向链表与单向链表非常类似,不过它们包含有另外的指针,以支持更多导航选项;给定双向链表中的一个节点,可以向前移动,也可以向后移动。这使得它们比单向链表更灵活,但也使用了更多内存,所以,除非确实需要这种灵活性,否则不要使用这种双向链表。GLib包含有一个名为GList的双向链表实现。GList中的大部分操作与GSList中的类似。我们将查看基本用法的一些示例,然后是GList所支持的附加操作。基本操作这里是使用GList可以进行的一些常见操作:/ex-glist-1.c#includeintmain(intargc,char*argv)GList*list

54、=NULL;list=g_list_append(list,Austin);printf(Thefirstitemis%sn,list-data);list=g_list_insert(list,Baltimore,1);printf(Theseconditemis%sn,g_list_next(list)-data);list=g_list_remove(list,Baltimore);printf(AfterremovalofBaltimore,thelistlengthis%dn,g_list_length(list);GList*other_list=g_list_append(NUL

55、L,Baltimore);list=g_list_concat(list,other_list);printf(Afterconcatenation:);g_list_foreach(list,(GFunc)printf,NULL);list=g_list_reverse(list);printf(nAfterreversal:);g_list_foreach(list,(GFunc)printf,NULL);g_list_free(list);return0;OutputThefirstitemisAustinTheseconditemisBaltimoreAfterremovalofBal

56、timore,thelistlengthis1Afterconcatenation:AustinBaltimoreAfterreversal:BaltimoreAustin上面的代码看起来非常熟悉!上面所有操作都在GSList中出现过;唯一的区别是GList的函数名是gist开头,而不是g_slist。当然,它们全都要使用指向GList结构体而不是指向GSList结构体。更好的导航已经了解了一些基本的GList操作后,这里是一些可能的操作,唯一的原因就是GList中的每个节点都有一个指向前一个节点的链接:/ex-glist-2.c#includeintmain(intargc,char*arg

57、v)GList*list=g_list_append(NULL,Austin);list=g_list_append(list,Bowie);list=g_list_append(list,Charleston);printf(Heresthelist:);g_list_foreach(list,(GFunc)printf,NULL);GList*last=g_list_last(list);printf(nThefirstitem(usingg_list_first)is%sn,g_list_first(last)-data);printf(Thenext-to-lastitemis%sn,

58、g_list_previous(last)-data);printf(Thenext-to-lastitemis%sn,g_list_nth_prev(last,1)-data);g_list_free(list);return0;OutputHeresthelist:AustinBowieCharlestonThefirstitem(usingg_list_first)isAustinThenext-to-lastitemisBowieThenext-to-lastitemisBowie没有太令人惊讶的事情,不过有一些注解:g_list_nth_pre的第二个参数是一个整数,表明需要向前导航

59、多少个节点。如果传递的值超出了GList的范围,那么要准备好处理程序的崩溃。g_list_first是一个O(n)操作。它从一个给定的节点开始向后搜索,直到找到GList的头。上面的示例是一个最坏的情形,因为遍历是从列表的末尾开始的。同理,g_list_last也是一个O(n)操作。使用链接删除节点已经了解了在拥有指向其容纳的数据的指针的前提下如何从列表中删除一个节点;可以很好地完成。如果拥有一个指向节点本身的指针,可以通过一个快速的0(1)操作直接删除那个节点。/ex-glist-tmain(intargc,char*argv)GLi*listist_append(NULL,Austin);

60、_append(list,Bowie);_append(list,Chicago);intf(Heresthist:);st_foreach(l,(GFunc)printf,NULL);GLi*bowiist_nth(list,1);ink(list,bowie);st_free_1(bowiee);intf(nHer1helistaftertheremove_linkcall:st_foreach(l,(GFunc)printf,NULL);_deink(li_nth(list,1);intf(nHer1helistafterthedelete_linkcall:);st_foreach(l

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论