Glib(2)

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() 的普通函数能节约你大量的代码、错误、以及时间。

一般情况下,你没有必要为 mallocg_malloc 指定具体的要申请的块的大小。一般都是使用 sizeof() 来告诉编译器或运行时系统申请某种类型的某个倍数。 为了与数据类型相符,你必须要将 malloc() 返回值进行强制转换。 强制转换要用大量的括号和星号,所以 GLib 提供了一些宏,如 g_new(),g_new0() 以及 g_renew() 。 下面是一些示例代码。

  1. typedef   struct  _footype footype;  
  2. footype *my_data;  
  3. /* Allocate space for three footype structures (long version) */   
  4. my_data = (footype *) g_malloc(sizeof (footype)*3);  
  5. /* The abbreviated version using g_new */   
  6. my_data = g_new(footype, 3);  
  7. /* To initialize the memory to 0, use g_new0 */   
  8. my_data = g_new0(footype, 3);  
  9. /* Expand this block of memory to four structures (long version) */   
  10. my_data = (footype *) g_realloc(my_data, sizeof (footype)*4);  
  11. /* Shorter version */   
  12. my_data = g_renew(my_data, 4);  
 

 

 

在上面的代码段中你可以清楚地看出, 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() 来申请块的例子:

  1. GMemChunk my_chunk;  
  2. my_chunk = g_mem_chunk_new("My Chunk" ,          /* name */   
  3.                            42,                 /* atom size */   
  4.                            42*16,              /* block size */   
  5.                            G_ALLOC_AND_FREE);  /* access mode */   

 

g_mem_chunk_new() 有四个参数,第一个参数是内存块的名字,第二个参数是原子内存的大小 ( 在这里是 42) ,第三个参数是块的总共大小,最后一个参数是访问模式。这个函数的返回值是指向 GMemChunk 的指针。

注意: GMemChunk 并不是一个数据结构。它是一个内存管理系统,它管理的内存片断里面含有数据结构。

第四个参数“访问模式”为你提供了如何创建和申请原子内存。一共有两种方式:

G_ALLOCC_AND_FREE 允许内存池在任何时间返回单个的原子内存。

G_ALLOC_ONLY 仅允许在处理整个内存块的时候申请原子内存。使用这个模式要比使用 G_ALLOC_AND_FREE 高效。

下面的例子指示了如何申请或释放原子内存。

  1. gchar *data[50000];  
  2. gint i;  
  3. /* allocate 40,000 atoms */   
  4. for (i = 0; i < 40000; i++)  
  5. {  
  6.   data[i] = g_mem_chunk_alloc(my_chunk);  
  7. }  
  8. /* allocate 10,000 more atoms and initialize them */   
  9. for (i = 40000; i < 50000; i++)  
  10. {  
  11.   data[i] = g_mem_chunk_alloc0(my_chunk);  
  12. }  
  13. /* free one atom */   
  14. g_mem_chunk_free(my_chunk, data[42]);  
 

 


在这里, 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()

 

有一些函数会一次性的在整个内存块上操作原子内存。下面的例子展示了这些函数的用法。

  1. /* free up any unused atoms */   
  2. g_mem_chunk_clean(my_chunk);  
  3. /* free all unused atoms in all memory chunks */   
  4. g_blow_chunks();  
  5. /* deallocate all atoms in a chunk */   
  6. g_mem_chunk_reset(my_chunk);  
  7. /* deallocate a memory chunk */   
  8. g_mem_chunk_destroy(my_chunk);  
 

 

 

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 为你提供了一些宏来减少你的输入。

  1. typedef   struct  _footype footype;  
  2. GMemChunk *pile_of_mem;  
  3. footype *foo;  
  4. /* create a memory chunk with space for 128 footype atoms */   
  5. pile_of_mem = g_mem_chunk_new("A pile of memory" ,  
  6.                              sizeof (footype),  
  7.                              sizeof (footype)*128,  
  8.                              G_ALLOC_AND_FREE);  
  9. /* the same thing, with g_mem_chunk_create */   
  10. /* the name will be "footype mem chunks (128)" */   
  11. pile_of_mem = g_mem_chunk_create(footype, 128, G_ALLOC_AND_FREE);  
  12. /* allocate an atom */   
  13. foo = (footype *) g_mem_chunk_alloc(pile_of_mem);  
  14. /* the same thing, with g_mem_chunk_new */   
  15. foo = g_mem_chunk_new(footype, pile_of_mem);  
  16. /* the same thing, but zero out the memory */   
  17. foo = g_mem_chunk_new0(footype, pile_of_mem);  
 

 

 

从上面这些代码中,可以很容易的明白这些宏的意图。注意,如果你知道原子内存的类型的话, 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 ,使用下面两个函数之一:

  1. GQuark quark;  
  2. gchar *string;  
  3. quark = g_quark_from_string(string);  
  4. quark = g_quark_from_static_string("string" );  
 

 

 

这两个函数都是以字符串作为它们唯一的参数。它们的区别是 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() ,因为这个字符串并不是一个拷贝。

下面是一个简短的示例代码:

  1. GQuark *my_quark = 0;  
  2. my_quark = g_quark_from_string("Chevre" );  
  3. if  (!g_quark_try( "Cottage Cheese" ))  
  4. {  
  5.   g_print("There isn't any quark for /"Cottage Cheese/"/n" );  
  6. }  
  7. g_print("my_quark is a representation of %s/n" , g_quark_to_string(my_quark));  
 

 

注意: 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)

拷贝 srcdest ,包括最后的 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 。如果 delimitersNULL 的话,这个函数会使用 " _-|<>. "; 这些是 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)

检查 formatargs ,返回格式化后所需要缓冲区的最大值。

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() 创建的字符数组。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值