2016年(2)
分类: LINUX
2016-08-08 15:10:52
GLib
1.1 Introduction
在开源世界中,G中很常见的。 它代表了GNU ("GNU's Not Unix")。 像GTK+,GLib,GObject,以及GNOME,还有一些其它的软件包,如Ghostscript和gcc中都充满了G。
为了理解后面的章节,你必须学习一些GLib的基础知识(libglib-2.0)。它为GTK+和GNOME程序提供了基础的数据结构和实用函 数。在本章中将会涉及到GLib的结构和API的介绍。 你将会在第二章中学习GLib's object system(GObject)。
在你使用GNOME和GTK+的时候,会不可避免的使用GLib。 其它的一些库,如ORBit除了GLib并没有引用其它的库。 GLib所提供的abstractions(这个不知道应该译成什么)和实用工具编程程序提供了方便, 而且很容易移植到其它平台。
在本章中并没有包含图形操作代码,它只是一个简单的,逐步讲述了GLib的函数和数据结构。 可能读起来有点枯燥,不过你也可以直接跳到第三章去阅读GTK+的内容。 但是你会经常的返回来查看这前两章的内容。
GLib Reference http://library.gnome.org/devel/glib/unstable/index.html
1.2 GLib Naming Coventions (GLib命名规则)
就像许多其它的库一下,GLib也为了一致性和易读性规范了命名规则。
函数的名字一般都是小写的,并且在每部分名字之间加下划线, 如g_timer_new(),g_list_append()。并且所有的函数名字都是以g_开头。
在GLib中,所有的函数都有前缀g_。
类型名并不包含下划线,并且GLib里面的所有类型组件都是以大写字母G开头的,如GTimer,GList。但GLib中的基本类型是值得注意的例外。在第三节中将会有介绍。
如果某个函数主要是操作某个特定的类型的话,这个函数的前缀就与相应的类型相匹配。例如,g_timer_*就是操作GTimer类型。 g_list_*就是操作GList类型。
这些规则听起来比实际要复杂。
1.3 Basic Types (基本类型)
在开始使用GLib之前,你首先要适应GLib的基本类型。 你或许很想知道为什么使用guchar要比使用unsigned char好。 如果你的程序一直待在同一个平台上执行,那么使用guchar与使用unsigned char并没有实质的差别。 但是,如果你想编写出在不同平台之间移植的程序,如Windows和Unix之间。 那你会很感谢GLib将基本类型给你抽象出来了。
例如,你想在所有可能的平台上定义16位的无符号整型,使用C语言的话可能看起来很麻烦。但幸运的是GLib帮你处理了这些。 所有的基本类型在下面的表格中列出。
要使用GLib和它的类型,必须要在源码中包含glib.h
#include
gpointer和gconstpointer类型在GLib的数据结构中经常出现,这两个是无类型的内存指针。在GLib中,函数负责检查这两个指针的类型,程序员和编译器并不管这些。这在回调函数以及排序和遍历中比较的时候尤其方便。
在GLib的头文件中为gboolean类型定义了TRUE和FALSE常量。 在使用这些常量的时候,不要使用比较运算符。 例如要用:if (my_gboolean),而不是:if (my_gboolean == TRUE)。
GLib Type Corresponding Type in C gchar char ugchar unsigned char gint int guint unsigned int gshort short gushort unsigned short glong long gulong unsigned long gfloat float gdouble double gint8 int, 8 bits wide guint8 unsigned int, 8 bits wide gint16 int, 16 bits wide guint16 unsigned int, 16 bits wide gint32 int, 32 bits wide guint32 unsigned int, 32 bits wide gint64 int, 64 bits wide guint64 unsigned int, 64 bits wide gpointer void *, untyped pointer gconstpointer const void *, constant untyped pointer gboolean Boolean value, either TRUE or FALSE
1.4 Basic Utilities
(基本函数,这个Utilities不知道如何译,就写成函数吧,因为后面确实在讲函数,嘿嘿……)
为了简化你的程序与C语言以及系统的交互,GLib提供了大量的函数。 要了解GLib的函数处理数据结构部分,请看1.5节。
1.4.1 内存管理
如果你使用GLib提供的内存管理例程,它可以避免你处理很多让你头痛的事。 GLib提供了的附加的错误检查与侦测。在下面的表格中为C程序员提供了一个参考。
你可以使用g_malloc(),g_realloc(),g_free()来代替malloc(),realloc(),free(),它们提供了相同的处理方式。为了将申请的内存在使用前清零,你可以使用g_malloc0()。 注意,它的语法看起来像malloc,而不是calloc()。
GLib Function Corresponding C Function gpointer g_malloc(gulong n_bytes) void *malloc(size_t size) with error handling gpointer g_malloc0(gulong n_bytes) like malloc(), but initializes memory as in calloc() gpointer g_try_malloc(gulong n_bytes) like malloc() without error checking gpointer g_realloc(gpointer mem, gulong n_bytes) void *realloc(void *ptr, size_t size) with error checking gpointer g_try_realloc(gpointer mem, gulong n_bytes) realloc() without error checking void g_free(gpointer mem) void free(void *ptr)
注意: 如果你有一些特殊的原因要使用函数的返回值的话,你可以使用g_try_malloc()和g_try_realloc(),如果出错的时候,它们会返回NULL。 你可以在某些不是非常关键的地方使用(如某些用来提高性能的额外缓冲区),或者某些测试的时候。
自然,如果你想覆盖GLib的保护机制,你必须要清楚你在做什么。 对大多数程序来说,这些像g_malloc()的普通函数能节约你大量的代码、错误、以及时间。
一般情况下,你没有必要为malloc或g_malloc指定具体的要申请的块的大小。一般都是使用sizeof()来告诉编译器或运行时系统申请某种类型的某个倍数。 为了与数据类型相符,你必须要将malloc()返回值进行强制转换。 强制转换要用大量的括号和星号,所以GLib提供了一些宏,如g_new(),g_new0()以及g_renew()。 下面是一些示例代码。
在上面的代码段中你可以清楚地看出,g_new()是g_malloc()的简化,g_renew()是g_realloc()的简易形式,g_new0()是g_malloc0()的简易形式。
警告:记住你在使用g_new()时需要一个类型,就像使用sizeof()一样。 有时候像这样的的语句会产生编译错误:
b = g_new(a, 1) (a只是一个变量,而不是类型)
产生错误的原因就是因为g_new只是一个宏,应该给它传递类型,而不是某个类型的变量。
内存块
GUI程序一般倾向于重复申请大小相同的内存块(原子)。而且,那儿有一些相关的原子内存(atom)。GLib使用了一种称为“memory chunks”的方法为程序提供了相应的原子内存。一个内存块由一些原子内存组成的;所以块的大小肯定是原子内存大小的整数倍。
这儿有一个使用g_mem_chunk_new()来申请块的例子:
g_mem_chunk_new()有四个参数,第一个参数是内存块的名字,第二个参数是原子内存的大小(在这里是42),第三个参数是块的总共大小,最后一个参数是访问模式。这个函数的返回值是指向GMemChunk的指针。
注意: GMemChunk并不是一个数据结构。它是一个内存管理系统,它管理的内存片断里面含有数据结构。
第四个参数“访问模式”为你提供了如何创建和申请原子内存。一共有两种方式:
G_ALLOCC_AND_FREE允许内存池在任何时间返回单个的原子内存。
G_ALLOC_ONLY仅允许在处理整个内存块的时候申请原子内存。使用这个模式要比使用G_ALLOC_AND_FREE高效。
下面的例子指示了如何申请或释放原子内存。
在这里,g_mem_chunk_alloc()和g_mem_chunk_alloc0()都可以申请单独的原子内存。这两个函数像g_malloc()和g_malloc0()一样,返回一个指向原子内存的指针,但是,他们使用GMemChunk结构而不是大小作为参数。g_mem_chunk_free()函数使用指向单独原子内存的指针来做为参数,它将释放的内存返回到内存池中。
警告: 使用g_mem_chunk_free来释放原子内存的时候,该原子内存所在的内存块必须是使用G_ALLOC_AND_FREE模式创建的。除此之外,使用g_free()来释放原子内存的时候将会引起一个段错误。产生段错误的原因是因为内存块的释放函数将会导致两次调用free()。
有一些函数会一次性的在整个内存块上操作原子内存。下面的例子展示了这些函数的用法。
g_mem_chunk_clean(chunk)会检查chunk并且释放那些不再使用的内存。这个例程给你提供了一些手工管理基本内存的方式。g_mem_chunk_free()函数并不必须立即释放原子内存。只有在方便或者必须的时候,GLib才会释放它。g_mem_chunk_clean()强迫它立即释放。
g_blow_chunks()会在程序中所有的outstanding内存块中运行g_mem_chunk_clean()。
g_mem_chunk_reset(chunk)会释放chunk中的所有原子内存,包括那些在使用的。你要小心使用这个函数,因为他可能会释放掉仍要使用的原子内存。
g_mem_chunk_destroy(chunk)会释放包括chunk本身以及chunk中的所有原子内存。
在内存管理上面,GLib为你提供了一些宏来减少你的输入。
从上面这些代码中,可以很容易的明白这些宏的意图。注意,如果你知道原子内存的类型的话,g_mem_chunk_create()是一个比g_mem_chunk_new()更简单的方法。还有,每个宏都会自动地将块名字拼凑起来。 g_mem_chunk_new()和g_mem_chunk_new0()是与g_new()和g_new0()相对应的用来操作内存块的函数。
如果你想知道当前内存块的统计数字,使用g_mem_chunk_print(chunk)可以得到一个简单的报告。 使用g_mem_chunk_info()可以得到所有内存块的详细信息。
1.4.2 Quarks (夸克)
为了在程序中标识一块数据,你一般有两种方式可选:数字或字符串。但是这两者都有一些缺点。数字是非常难以辨认的。如果你开始粗略的知道需要多少标签,你就可以定义一个枚举类型和一些字符符号。但是,你没法在运行的时候动态添加标签。
另一方面,你可以在运行的时候动态的添加或修改字符串,而且它们是很容易理解的。 但是,字符串比较要比数字比较花更长的时间,而且在内存中管理字符串有一些你可能不愿意处理的额外麻烦。
GLib提供了GQuark类型,它整合了数字的简单和字符串的易用。在它内部,它就是一个易于比较和复制的整形数。GLib将这些数字映射为你的字符串,并且你可以在任何时间取得字符串所对应的值。
要创建一个quark,使用下面两个函数之一:
这两个函数都是以字符串作为它们唯一的参数。它们的区别是g_quark_from_string()会在映射的时候创建一个字符串的拷贝,但是g_quark_from_static_string()并不会。
警告:小心使用g_quark_from_static_string()。在每次执行它的时候会节约很少的CPU和内存,但是,在你的程序中增加了附加的依赖性可能会导致你的程序有一些bug,所以,或许节约的那一点开销并不值得你去使用该函数。
如果你想检验某个字符串是否有一个quark值,调用:
g_quark_try_string(string)
这个函数的返回值是该字符串所对应的quark值。如果返回0的话,说明没有与这个字符串相对应的quark值。
从quark恢复到string使用:
string = g_quark_to_string(quark);
如果它执行成功,它将会返回quark对应的字符串的指针。但是你不能在这个指针上调用free(),因为这个字符串并不是一个拷贝。
下面是一个简短的示例代码:
注意:GQuark值是分配给字符串的数字,并且很容易测试它们的等同性。然而,它们并没有数字顺序。你不能用quark值来进行字母顺序测试。因此,你不能用它们作为排序关键字。如果你想比较quark所代表的字符串,你必须先用g_quark_to_string()来提取相应的字符串,然后才可以使用strcmp()或g_ascii_strcasecmp()。
1.4.3 C字符串
GLib提供了一些字符串函数来与标准C库进行交互(不要对GString疑惑,在1.5.1节将会讲到)。你可以用这些字符串函数来扩充或代替stringf()、strdup()或strstr()等。
下面的这些函数会返回一个新的字符串缓冲区的指针,所以你在使用完后必须要释放它们。
gchar *g_strdup(const gchar *str)
复制str并返回它的一个拷贝。
gchar *g_strndup(const gchar *str, gsize n)
返回str中前n个字符的一个拷贝。这个拷贝总会在最后附加一个NULL结束符。
gchar *strnfill(gsize length, gchar *fill_char)
创建一个长度为length并被fill_char填充的字符串。
gchar *g_strdup_printf(const gchar *format, ...)
像sprintf()一样格式化字符串和参数。但是,你没必要像sprintf()一样创建和指定一个缓冲区,GLib将这些自动做了。
gchar *g_strdup_vprintf(const gchar *format, va_list args)
类似于上面的那个函数,它跟vsprintf()类似,使用C的可变参数能力,有关可变参数可以在stdarg(3)的手册中找到。
gchar *g_strescape(const gchar *source, const gchar *exception)
将一些特殊控制字符转换成相应的ASCII,如,将Tab转成\t,这些转换有:退格(\b)、换页(\f)、换行(\n)、回车(\r)、反斜线(\变成\\),双引号(" 变成 \")。任何附加的非ASCII字符会转换成相应的8进制表示(例如escape会变成\27)。你可以在字符串的exceptions中指定任何特定的例外。
gchar *g_strcompress(const gchar *source)
与g_strescape()相反,它是将ASCII格式的字符串转为真正的控制字符。
gchar *g_strconcat(const gchar *string1, ..., NULL)
它接受任意数量的string作为参数,并返回它们的连接后的字符串。你必须将NULL作为这个函数的最后一个参数。
gchar *g_strjoin(const gchar *separator, ..., NULL)
连接一些字符串,并且添加分隔符在每个字符串之间。如gstrjoin("|", "foo", "bar", NULL)会产生"foo|bar"。像g_strconcat(),你也必须将NULL作为最后一个参数传给这个函数。如果将separtor参数设为NULL的话,g_strjoin()就会等同于g_strconcat()了。
在下面的函数中,你应该为返回的结果申请空间。GLib并不会返回一个拷贝给你。它们与C相对应的函数非常像,参数要包含一个足够大的缓冲区来进行字符串处理。
gchar *g_stpcpy(gchar *dest, const gchar *src)
拷贝src到dest,包括最后的NULL字符。如果它执行成功,会返回dest中结束符拷贝的指针。它在进行高效的字符串连接时是非常有用的。
gint g_snprintf(gchar *string, gulong n, const gchar *format, ...)
像snprintf()一样,你必须确保string有足够大的空间。而且你必须要用n来指定这个缓冲区的大小。返回值是输出字符串的长度,也有可能这个输出字符串为了适应缓冲区的大小而被截断。这是C99的标准,并不只是你机子上传统C库的行为。
gint g_vsnprintf(gchar *string, gulong n, const gchar *format, va_list list)
跟上个函数类似,不过是变长参数。
gchar *g_strreverse(gchar *string)
将string里面的字符顺序反转。返回值仍然是string。
gchar *g_strchug(gchar *string)
将string开头的空白字符都移除。将string中相应的字符进行左移,返回string。
gchar *g_strchomp(gchar *string)
将string结尾的空格都删掉,返回string
gchar *g_strstrip(gchar *string)
将string开头的结尾的空白字符都删掉,返回string。
gchar *g_strdelimit(gchar *string, const gchar *delimiters, gchar *new_delimiter)
将string中的delimiters替换为new_delimiter。如果delimiters是NULL的话,这个函数会使用" _-|<>. "; 这些是G_STR_DELIMITERS中的标准集。返回值是string。
gchar *g_strcanon(gchar *string, const gchar *valid_chars, gchar *substituter)
将string中的,不属于valid_chars中字符的那些字符都替换为substituer。返回string。注意,这个函数是g_strdelimit的一个补充。
在下面的函数中,除了g_ascii_dtostr()之外,都不改变它们的参数。
gchar *g_strstr_len(const gchar *haystack, gssize haystack_len, const gchar *needle)
在haystack中遍历haystack_len长度,如果找到了needle字串,则返回这个位置的指针,如果没有找到则返回NULL。
gchar *g_strrstr(const gchar *haystack, const gchar *needle)
类似于上个函数,这个函数将会从后面开始查找,但是它并没有haystack_len参数。
gchar *g_strrstr_len(gchar *haystack, gssize haystack_len, gchar *needle)
与g_strrstr()相同,但是它只在前haystack_len个字符中查找。
gsize g_printf_string_upper_bound(const gchar *format, va_list args)
检查format和args,返回格式化后所需要缓冲区的最大值。
gdouble g_ascii_strtod(const gchar *nptr, gchar **endptr)
将string转为双字长度的浮点数。如果你提供了一个有效的endptr指针地址,这个函数会将指针设置到string中被转换的最后一个字符的位置。与strtod()的区别是这个函数忽略了C locale。
gchar *g_ascii_dtostr(gchar *buffer, gint buf_len, gdouble d)
将d转换为ASCII字串。将转换后的忽略C locale然后写入到buffer中,最大长度为buf_len。目标字串的长度永远不会超过G_ASCII_DTOSTR_BUF_SIZE。这个函数返回buffer的指针。
注意:使用g_ascii_strtod()和g_ascii_dtostr()来读写文件或数据流,并不都是人可读的。 因为这些函数使用统一的标准,是区域无关的格式,为了解决某些特定的问题。 例如,一些人将本地设为German,然后运行你的程序,你无须担心本地化的数字之间逗号和句点的意义转换。
最后,这儿列出一些操作字符串数组的函数。 NULL指针来表示这些数组的结束。
gchar **g_strsplit(const gchar *string, const gchar *delimiter, gint max_tokens)
使用delimiter来将string切割成至多max_tokens个部分。返回值是新申请的一个字符串数组,用来保存被切割的这些部分。这个字符串数组必须由你自己释放。 如果输入字符串是空的,这个返回值也是一个空的数组。
gchar *g_str_joinv(const gchar *separator, gchar **str_array)
将字符串数组组合成单个字符串,并将这个新申请的字符串返回。如果separator不空,g_str_joinv()会在每个字符串之间添加上一个separator分隔符。
gchar **g_strdupv(gchar **str_array)
返回str_array的一个完整拷贝。
void **g_strfreev(gchar **str_array)
释放str_array数组以及这些字符串。
警告: 除了g_strfreev()之外,不要使用其它的函数来释放像g_strsplit()或g_strdupv()创建的字符数组