GTK+中文社区(gtk.awaysoft.com)

 找回密码
 马上加入

QQ登录

只需一步,快速开始

查看: 3635|回复: 0

GLib(二)

[复制链接]
  • TA的每日心情
    奋斗
    2016-10-11 09:20
  • 签到天数: 271 天

    连续签到: 1 天

    [LV.8]以坛为家I

    发表于 2011-7-8 00:58:41 | 显示全部楼层 |阅读模式
    转自:http://blog.csdn.net/ciahi/article/details/4092179



    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()。 下面是一些示例代码。


      [li]
      typedef
      struct
      _footype footype;  
      [/li][li]
      footype *my_data;  
      [/li][li]
      /* Allocate space for three footype structures (long version) */
        
      [/li][li]
      my_data = (footype *) g_malloc(
      sizeof
      (footype)*3);  
      [/li][li]
      /* The abbreviated version using g_new */
        
      [/li][li]
      my_data = g_new(footype, 3);  
      [/li][li]
      /* To initialize the memory to 0, use g_new0 */
        
      [/li][li]
      my_data = g_new0(footype, 3);  
      [/li][li]
      /* Expand this block of memory to four structures (long version) */
        
      [/li][li]
      my_data = (footype *) g_realloc(my_data,
      sizeof
      (footype)*4);  
      [/li][li]
      /* Shorter version */
        
      [/li][li]
      my_data = g_renew(my_data, 4);  
      [/li]




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





      [li]
      GMemChunk my_chunk;  
      [/li][li]
      my_chunk = g_mem_chunk_new(
      "My Chunk"
      ,         
      /* name */
        
      [/li][li]
                                 42,                 
      /* atom size */
        
      [/li][li]
                                 42*16,              
      /* block size */
        
      [/li][li]
                                 G_ALLOC_AND_FREE);  
      /* access mode */
        
      [/li]




    g_mem_chunk_new()有四个参数,第一个参数是内存块的名字,第二个参数是原子内存的大小(在这里是42),第三个参数是块的总共大小,最后一个参数是访问模式。这个函数的返回值是指向GMemChunk的指针。
    注意: GMemChunk并不是一个数据结构。它是一个内存管理系统,它管理的内存片断里面含有数据结构。
    第四个参数“访问模式”为你提供了如何创建和申请原子内存。一共有两种方式:
    G_ALLOCC_AND_FREE允许内存池在任何时间返回单个的原子内存。
    G_ALLOC_ONLY仅允许在处理整个内存块的时候申请原子内存。使用这个模式要比使用G_ALLOC_AND_FREE高效。
    下面的例子指示了如何申请或释放原子内存。



      [li]
      gchar *data[50000];  
      [/li][li]
      gint i;  
      [/li][li]
      /* allocate 40,000 atoms */
        
      [/li][li]
      for
      (i = 0; i < 40000; i++)  
      [/li][li]
      {  
      [/li][li]
        data = g_mem_chunk_alloc(my_chunk);  
      [/li][li]
      }  
      [/li][li]
      /* allocate 10,000 more atoms and initialize them */
        
      [/li][li]
      for
      (i = 40000; i < 50000; i++)  
      [/li][li]
      {  
      [/li][li]
        data = g_mem_chunk_alloc0(my_chunk);  
      [/li][li]
      }  
      [/li][li]
      /* free one atom */
        
      [/li][li]
      g_mem_chunk_free(my_chunk, data[42]);  
      [/li]







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

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


      [li]
      /* free up any unused atoms */
        
      [/li][li]
      g_mem_chunk_clean(my_chunk);  
      [/li][li]
      /* free all unused atoms in all memory chunks */
        
      [/li][li]
      g_blow_chunks();  
      [/li][li]
      /* deallocate all atoms in a chunk */
        
      [/li][li]
      g_mem_chunk_reset(my_chunk);  
      [/li][li]
      /* deallocate a memory chunk */
        
      [/li][li]
      g_mem_chunk_destroy(my_chunk);  
      [/li]




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

      [li]
      typedef
      struct
      _footype footype;  
      [/li][li]
      GMemChunk *pile_of_mem;  
      [/li][li]
      footype *foo;  
      [/li][li]
      /* create a memory chunk with space for 128 footype atoms */
        
      [/li][li]
      pile_of_mem = g_mem_chunk_new(
      "A pile of memory"
      ,  
      [/li][li]
                                   
      sizeof
      (footype),  
      [/li][li]
                                   
      sizeof
      (footype)*128,  
      [/li][li]
                                   G_ALLOC_AND_FREE);  
      [/li][li]
      /* the same thing, with g_mem_chunk_create */
        
      [/li][li]
      /* the name will be "footype mem chunks (128)" */
        
      [/li][li]
      pile_of_mem = g_mem_chunk_create(footype, 128, G_ALLOC_AND_FREE);  
      [/li][li]
      /* allocate an atom */
        
      [/li][li]
      foo = (footype *) g_mem_chunk_alloc(pile_of_mem);  
      [/li][li]
      /* the same thing, with g_mem_chunk_new */
        
      [/li][li]
      foo = g_mem_chunk_new(footype, pile_of_mem);  
      [/li][li]
      /* the same thing, but zero out the memory */
        
      [/li][li]
      foo = g_mem_chunk_new0(footype, pile_of_mem);  
      [/li]




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

      [li]
      GQuark quark;  
      [/li][li]
      gchar *string;  
      [/li][li]
      quark = g_quark_from_string(string);  
      [/li][li]
      quark = g_quark_from_static_string(
      "string"
      );  
      [/li]




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


      [li]
      GQuark *my_quark = 0;  
      [/li][li]
      my_quark = g_quark_from_string(
      "Chevre"
      );  
      [/li][li]
      if
      (!g_quark_try(
      "Cottage Cheese"
      ))  
      [/li][li]
      {  
      [/li][li]
        g_print(
      "There isn't any quark for /"
      Cottage Cheese/
      "/n"
      );  
      [/li][li]
      }  
      [/li][li]
      g_print(
      "my_quark is a representation of %s/n"
      , g_quark_to_string(my_quark));  
      [/li]



    注意: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()创建的字符数组。
    *滑块验证:
    您需要登录后才可以回帖 登录 | 马上加入

    本版积分规则

    申请友链|Archiver|小黑屋|手机版|GTK+中文社区 ( 粤ICP备13080851号 )

    我要啦免费统计

    GMT+8, 2024-12-22 00:03 , Processed in 0.058820 second(s), 7 queries , Redis On.

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表