您的位置:首页 > 其它

Glib相关知识

2014-09-27 17:37 120 查看

Glib相关知识

参考网址(GLib Reference Manual):
http://developer.gnome.org/glib/2.28/
(By Ksir 2011-4-29)

一、什么是glib库
glib库是linux平台下最常用的c语言函数库,它具有很好的可移植性和实用性。glib是gtk+库和gnome的基础。glib的各种实用程序具有一致的接口。glib为许多标准的、常用的C语言结构提供了相应的替代物。它的编码风格是半面向对象,标识符加了一个前缀“g”,这也是一种通行的命名约定。使glib库的程序都应该包含glib的头文件glib.h。如果程序已经包含了gtk
.h则不需要再包含glib.h。
二、glib的类型定义
glib的类型定义不是使用C的标准类型,它自己有一套类型系统。它们比常用的C语言的类型更丰富,也更安全可靠。引进这套系统是为了多种原因。
以下是glib基本类型定义:
整数类型:gint8、guint8、gint16、guint16、gint32、guint32、gint64、guint64。其中gint8是8位的整数,guint8是8位的无符号整数,其他依此类推。这些整数类型能够保证大小。不是所有的平台都提供64位整型,如果一个平台有这些, glib会定义G_H***E_GINT64。
整数类型gshort、glong、gint和short、long、int完全等价。
布尔类型gboolean:它可使代码更易读,因为普通C没有布尔类型。Gboolean可以取两个值:TRUE和FALSE。实际上FALSE定义为0,而TRUE定义为非零值。
字符型gchar和char完全一样,只是为了保持一致的命名。
浮点类型gfloat、gdouble和float、double完全等价。
指针gpointer对应于标准C的void *,但是比void *更方便。
指针gconst pointer对应于标准C的const void *(注意,将const void *定义为const gpointer是行不通的)。

三、glib的宏

一些常用的宏列表

#include <glib.h>

TRUE

FALSE

NULL

MAX(a, b)

MIN(a, b)

ABS ( x )

CLAMP(x, low, high)

TRUE / FALSE / NULL就是1 / 0 / ( ( v o i d * ) 0 )。

MIN ( ) / MAX ( )返回更小或更大的参数。

ABS ( )返回绝对值。

CLAMP(x,low,high )若X在[low,high]范围内,则等于X;如果X小于low,则返回low;如果X大于high,则返回high。

有些宏只有g l i b拥有,例如在后面要介绍的gpointer-to-gint和gpointer-to-guint。

大多数glib的数据结构都设计成存储一个gpointer。如果想存储指针来动态分配对象,可以这样做。

在某些情况下,需要使用中间类型转换。

//////////////////////////////////////////////////////////////

gint my_int;

gpointer my_pointer;

my_int = 5;

my_pointer = GINT_TO_POINTER(my_int);

printf("We are storing %dn", GPOINTER_TO_INT(my_pointer));

//////////////////////////////////////////////////////////////

这些宏允许在一个指针中存储一个整数,但在一个整数中存储一个指针是不行的。

如果要实现的话,必须在一个长整型中存储指针。

宏列表:

在指针中存储整数的宏

#include <glib.h>

GINT_TO_POINTER ( p )

GPOINTER_TO_INT ( p )

GUINT_TO_POINTER ( p )

GPOINTER_TO_UINT ( p )

调试宏:

定义了G_DISABLE_CHECKS或G_DISABLE_ASSERT之后,编译时它们就会消失.

宏列表:

前提条件检查

#include <glib.h>

g_return_if_fail ( condition )

g_return_val_if_fail(condition, retval)

///////////////////////////////////////////////////////////////////////////////////

使用这些函数很简单,下面的例子是g l i b中哈希表的实现:

void g_hash_table_foreach (GHashTable *hash_table,GHFunc func,gpointer user_data)

{

GHashNode *node;

gint i;

g_return_if_fail (hash_table != NULL);

g_return_if_fail (func != NULL);

for (i = 0; i < hash_table->size; i++)

for (node = hash_table->nodes[i]; node; node = node->next)

(* func) (node->key, node->value, user_data);

}

///////////////////////////////////////////////////////////////////////////////////

宏列表:

断言

#include <glib.h>

g_assert( condition )

g_assert_not_reached ( )

如果执行到这个语句,它会调用abort()退出程序并且(如果环境支持)转储一个可用于调试的core文件。

断言与前提条件检查的区别:

应该断言用来检查函数或库内部的一致性。

g_return_if_fail()确保传递到程序模块的公用接口的值是合法的。

如果断言失败,将返回一条信息,通常应该在包含断言的模块中查找错误;

如果g_return_if_fail()检查失败,通常要在调用这个模块的代码中查找错误。

//////////////////////////////////////////////////////////////////////

下面glib日历计算模块的代码说明了这种差别:

GDate * g_date_new_dmy (GDateDay day, GDateMonth m, GDateYear y)

{

GDate *d;

g_return_val_if_fail (g_date_valid_dmy (day, m, y), NULL);

d = g_new (GDate, 1);

d->julian = FALSE;

d->dmy = TRUE;

d->month = m;

d->day = day;

d->year = y;

g_assert (g_date_valid (d));

return d;

}

//////////////////////////////////////////////////////////////////////

开始的预条件检查确保用户传递合理的年月日值;

结尾的断言确保glib构造一个健全的对象,输出健全的值。

断言函数g_assert_not_reached() 用来标识“不可能”的情况,通常用来检测不能处理的

所有可能枚举值的switch语句:

switch (val)

{

case FOO_ONE:

break ;

case FOO_TWO:

break ;

default:



gint len;

} ;

用下面的函数创建新的GString变量:

GString *g_string_new( gchar *init );

这个函数创建一个GString,将字符串值init复制到GString中,返回一个指向它的指针。

如果init参数是NULL,创建一个空GString。

void g_string_free( GString *string,gint free_segment );

这个函数释放string所占据的内存。free_segment参数是一个布尔类型变量。

如果free_segment参数是TRUE,它还释放其中的字符数据。

GString *g_string_assign( GString *lval,const gchar *rval );

这个函数将字符从rval复制到lval,销毁lval的原有内容。

注意,如有必要, lval会被加长以容纳字符串的内容。

下面的函数的意义都是显而易见的。其中以_c结尾的函数接受一个字符,而不是字符串。

截取string字符串,生成一个长度为l e n的子串:

GString *g_string_truncate( GString *string,gint len );

将字符串val追加在string后面,返回一个新字符串:

GString *g_string_append( GString *string,gchar *val );

将字符c追加到string后面,返回一个新的字符串:

GString *g_string_append_c( GString *string,gchar c );

将字符串val插入到string前面,生成一个新字符串:

GString *g_string_prepend( GString *string,gchar *val );

将字符c插入到string前面,生成一个新字符串:

GString *g_string_prepend_c( GString *string,gchar c );

将一个格式化的字符串写到string中,类似于标准的sprintf函数:

void g_string_sprintf( GString *string,gchar *fmt,. . . ) ;

将一个格式化字符串追加到string后面,与上一个函数略有不同:

void g_string_sprintfa ( GString *string,gchar *fmt,... );

################################## 计时器函数 ##################################

创建一个新的计时器:

GTimer *g_timer_new( void );

销毁计时器:

void g_timer_destroy( GTimer *timer );

开始计时:

void g_timer_start( GTimer *timer );

停止计时:

void g_timer_stop( GTimer *timer );

计时重新置零:

void g_timer_reset( GTimer *timer );

获取计时器流逝的时间:

gdouble g_timer_elapsed( GTimer *timer,gulong *microseconds );

################################## 错误处理函数 ##################################

gchar *g_strerror( gint errnum );

返回一条对应于给定错误代码的错误字符串信息,例如“ no such process”等。

使用g_strerror函数:

g_print("hello_world:open:%s:%sn", filename, g_strerror(errno));

void g_error( gchar *format, ... );

打印一条错误信息。

格式与printf函数类似,但是它在信息前面添加“ ** ERROR **: ”,然后退出程序。它只用于致命错误。

void g_warning( gchar *format, ... );

与上面的函数类似,在信息前面添加“ ** WARNING **:”,不退出应用程序。它可以用于不太严重的错误。

void g_message( gchar *format, ... );

在字符串前添加“message: ”,用于显示一条信息。

gchar *g_strsignal( gint signum );

打印给定信号号码的Linux系统信号的名称。在通用信号处理函数中很有用。

################################## 其他实用函数 ##################################

glib还提供了一系列实用函数,可以用于获取程序名称、当前目录、临时目录等。

这些函数都是在glib.h中定义的。

/* 返回应用程序的名称* /

gchar* g_get_prgname (void);

/* 设置应用程序的名称* /

void g_set_prgname (const gchar *prgname);

/* 返回当前用户的名称* /

gchar* g_get_user_name (void);

/* 返回用户的真实名称。该名称来自“passwd”文件。返回当前用户的主目录* /

gchar* g_get_real_name (void);

/* 返回当前使用的临时目录,它按环境变量TMPDIR、TMPandTEMP 的顺序查找。

如果上面的环境变量都没有定义,返回“ / t m p”* /

gchar* g_get_home_dir (void);

gchar* g_get_tmp_dir (void);

/* 返回当前目录。返回的字符串不再需要时应该用g_free ( ) 释放* /

gchar* g_get_current_dir (void);

/ *获得文件名的不带任何前导目录部分的名称。它返回一个指向给定文件名字符串的指针* /

gchar* g_basename (const gchar *file_name);

/* 返回文件名的目录部分。如果文件名不包含目录部分,返回“ .”。

* 返回的字符串不再使用时应该用g_free() 函数释放* /

gchar* g_dirname (const gchar *file_name);

/* 如果给定的file_name是绝对文件名(包含从根目录开始的完整路径,比如/usr/local),返回TRUE * /

gboolean g_path_is_absolute (const gchar *file_name);

/* 返回一个指向文件名的根部标志(“/”)之后部分的指针。

* 如果文件名file_name不是一个绝对路径,返回NULL * /

gchar* g_path_skip_root (gchar *file_name);

/ *指定一个在正常程序终止时要执行的函数* /

void g_atexit (GVoidFunc func);

上面介绍的只是glib库中的一小部分, glib的特性远远不止这些。

如果想了解其他内容,参考glib.h文件。这里面的绝大多数函数都是简明易懂的。

另外,http://www.gtk.org上的glib文档也是极好的资源。

如果你需要一些通用的函数,但glib中还没有,考虑写一个glib风格的例程,将它贡献到glib库中!你自己,以及全世界的glib使用者,都将因为你的出色工作而受益。

glib提供许多有用的函数及定义. 我把它们列在这里并做简短的解释. 很多都是与libc重复, 对这些我不再详述. 这些大致上是用来参考, 您知道有什麽东西可以用就好. 

17.1 定义 

为保持资料型态的一致, 这里有一些定义: 

G_MINFLOAT

G_MAXFLOAT

G_MINDOUBLE

G_MAXDOUBLE

G_MINSHORT

G_MAXSHORT

G_MININT

G_MAXINT

G_MINLONG

G_MAXLONG

此外, 以下的typedefs. 没有列出来的是会变的, 要看是在那一种平台上. 如果您想要具有可移植性, 记得避免去使用sizeof(pointer). 例如, 一个指标在Alpha上是8 bytes, 但在Inter上为4 bytes. 

char   gchar;

short  gshort;

long   glong;

int    gint;

char   gboolean;

unsigned char   guchar;

unsigned short  gushort;

unsigned long   gulong;

unsigned int    guint;

float   gfloat;

double  gdouble;

long double gldouble;

void* gpointer;

gint8

guint8

gint16

guint16

gint32

guint32

17.2 双向链结串列 

以下函数用来产生, 管理及销毁双向链结串列. 

GList* g_list_alloc(void);
void  g_list_free(GList *list);
void  g_list_free_1(GList *list);
GList* g_list_append(GList *list, gpointer data);                 
GList* g_list_prepend(GList *list, gpointer data);                
GList* g_list_insert(GList *list, gpointer data, gintposition);
GList* g_list_remove(GList *list, gpointer data);                    
GList* g_list_remove_link(GList *list, GList *link);
GList* g_list_reverse(GList *list);
GList* g_list_nth(GList *list, gint n);                          
GList* g_list_find(GList *list, gpointer data);
GList* g_list_last(GList *list);
GList* g_list_first(GList *list);
gint  g_list_length(GList *list);
void  g_list_foreach(GList *list, GFunc func, gpointer user_data);


17.3 单向链结串列 

以下函数是用来管理单向链结串列: 

GSList* g_slist_alloc(void);
void   g_slist_free(GSList *list);
void   g_slist_free_1(GSList *list);
GSList* g_slist_append(GSList *list, gpointer data);            
GSList* g_slist_prepend(GSList *list,  gpointer data);                    
GSList* g_slist_insert(GSList *list,  gpointer data, gint position);      
GSList* g_slist_remove(GSList *list,gpointer data);                        
GSList* g_slist_remove_link(GSList *list, GSList *link);              
GSList* g_slist_reverse(GSList *list);
GSList* g_slist_nth(GSList *list, gint n);                           
GSList* g_slist_find(GSList *list, gpointer data);                             
GSList* g_slist_last(GSList *list);
gint g_slist_length(GSList *list);
void g_slist_foreach(GSList *list, GFunc func, gpointer user_data);


17.4 记忆体管理 

gpointer g_malloc(gulong size);

这是替代malloc()用的. 你不需要去检查返回值, 因为它已经帮你做好了, 保证. 

gpointer g_malloc0     (gulong    size);

一样, 不过会在返回之前将记忆体归零. 

gpointer g_realloc     (gpointer  mem, gulong    size);

重定记忆体大小. 

void     g_free(gpointer  mem);

void     g_mem_profile (void);

将记忆体的使用状况写到一个档案, 不过您必须要在glib/gmem.c里面, 加#define MEM_PROFILE, 然後重新编译. 

void     g_mem_check   (gpointer  mem);

检查记忆体位置是否有效. 您必须要在glib/gmem.c上加#define MEM_CHECK, 然後重新编译. 

17.5 Timers 

Timer函数.. 

GTimer* g_timer_new(void);
void g_timer_destroy(GTimer *timer);
void g_timer_start(GTimer *timer);
void g_timer_stop(GTimer *timer);
void g_timer_reset(GTimer *timer);
gdouble g_timer_elapsed(GTimer *timer, gulong *microseconds);
17.6 字串处理 

GString* g_string_new(gchar *init);
void   g_string_free(GString *string, gint free_segment);                             
GString* g_string_assign(GString *lval, gchar *rval);                             
GString* g_string_truncate(GString *string, gint len);                             
GString* g_string_append(GString *string, gchar *val);                           
GString* g_string_append_c(GString *string, gchar c);
GString* g_string_prepend(GString *string, gchar *val);                             
GString* g_string_prepend_c(GString *string, gchar c);        
void   g_string_sprintf(GString *string, gchar *fmt, ...);        
void g_string_sprintfa(GString *string, gchar *fmt, ...);


17.7 工具及除错函数 

gchar* g_strdup    (const gchar *str);

gchar* g_strerror  (gint errnum);

我建议您使用这个来做所有错误讯息. 这玩意好多了. 它比perror()来的具有可移植性. 输出为以下形式: 

program name:function that failed:file or further description:strerror

这里是"hello world"用到的一些函数: 

g_print("hello_world:open:%s:%sn", filename, g_strerror(errno));

void g_error   (gchar *format, ...);

显示错误讯息, 其格式与printf一样, 但会加个"** ERROR **: ", 然後离开程式. 只在严重错误时使用. 

void g_warning (gchar *format, ...);

跟上面一样, 但加个"** WARNING **: ", 不离开程式. 

void g_message (gchar *format, ...);

加个"message: ". 

void g_print   (gchar *format, ...);

printf()的替代品. 

最後一个: 

gchar* g_strsignal (gint signum);

列印Unix系统的信号名称, 在信号处理时很有用. 

这些大都从glib.h中而来.
三、内存管理
glib用自己的g_变体包装了标准的malloc( )和free( ),即g_malloc() 和g_free( )。它们有以下几个小优点:
• g_malloc()总是返回gpointer,而不是char *,所以不必转换返回值。
• 如果低层的malloc ( )失败,g_malloc ( )将退出程序,所以不必检查返回值是否是NULL。
• g_malloc() 对于分配0字节返回NULL。
• g_free()忽略任何传递给它的NULL指针。
四、Glib编译
GLib的最新版本是GLib2.2.1,可以到www.gtk.org网站下载其源代码。使用GLib2.0编写的应用程序,在编译时应该在编译命令中加入`pkg-config -cflags -libs glib-2.0`,如编译一个名为hello.c的程序,输出名为hello的可执行文件,则命令为:
gcc `pkg-config -cflags -libs glib-2.0` hello.c -o hello

在GLIB中将线程(gthread),插件(gmoudle)和对象系统(gobject)这三个子系统区别对待,编译时要注意加入相应的参数。
如程序中用到对象系统,编译时就应加入:
`pkg-config --cflags --libs gobject-2.0`
用到线程,编译时则加入:
`pkg-config --cflags --libs gthread-2.0`
用到插件,编译时则加入:
`pkg-config --cflags --libs gmoudle-2.0`
五、对核心应用的支持
(来自http://www.ibm.com/developerworks/cn/linux/l-glib/index.html
GLib对核心应用的支持包括事件循环、内存操作、线程操作、动态链接库的操作和出错处理与日志等。
下面代码演示了事件循环、内存操作、线程这三种功能的简单应用:
#include <glib.h>

static GMutex *mutex = NULL;
static gboolean t1_end = FALSE;
static gboolean t2_end = FALSE;
typedef struct _Arg Arg;

struct _Arg
{
    GMainLoop* loop;
    gint max;
};

void  run_1(Arg *arg)
{
    int i ; 

    for(i=0; i < arg->max; i++)
    {
        if( g_mutex_trylock(mutex) == FALSE)
        {
            //g_print("%d : thread 2 locked the mutex /n", i);

            g_print("%d :thread 2 lock mutex\n", i);
            g_mutex_unlock(mutex);
        }
        else
        {
            g_usleep(10);
        }
    }
    t1_end = TRUE;
}

void  run_2(Arg *arg)
{
    int i;
    for(i=0; i<arg->max; i++)
    {
        if(g_mutex_trylock(mutex) == FALSE)
        {
            //g_print("%d : thread 1 locked mutex /n", i);
            g_print("%d :thread 1 lock mutex\n", i);
            g_mutex_unlock(mutex);
        }
        else
        {
            g_usleep(10);
        }
    }
    t2_end = TRUE;
}

void run_3(Arg *arg)
{
    for(;;)
    {
        if(t1_end && t2_end)
        {
            g_main_loop_quit(arg->loop);
            break;
        }
    }
}

int main(int argc, char *argv[])
{
    GMainLoop *mloop;
    Arg *arg;

    if(!g_thread_supported())
        g_thread_init(NULL);
    mloop = g_main_loop_new(NULL, FALSE);

    arg = g_new(Arg, 1);
    arg->loop = mloop;
    arg->max = 11;  
    mutex = g_mutex_new();
    g_thread_create(run_1, arg, TRUE, NULL);
    g_thread_create(run_2, arg, TRUE, NULL);
    g_thread_create(run_3, arg, TRUE, NULL);

    g_main_loop_run(mloop);
    g_print("Thread 3 exit event loop\n");
    g_mutex_free(mutex);
    g_print("free mutex\n");
    g_free(arg);
    g_print("free all memory\n");
}
makefile的编写
glibTest:glibTest.o
	gcc -o $@ $^ `pkg-config --cflags --libs glib-2.0 gthread-2.0`
glibTest.o:glibTest.c  
	gcc -o $@ -c $< `pkg-config --cflags --libs glib-2.0 gthread-2.0`
下面为输出结果:



以上例程创建了三个线程,其中run_1和run_2操作互斥对象,run_3检索前两个线程是否结束,如结束的话,则执行g_main_loop_quit退出事件循环。由于线程的运行是不确定的,所以不一定每次都是这一输出结果。
首先定义一个结构类型来保存创建的事件循环的对象指针和线程运行时的最多循环次数,一般情况下,如果为此数据结构来分配内存的话,用Arg *arg = (Arg *)malloc(sizeof(Arg));,释放时用free(arg);,这种传统的做法曾经让很多C语言的初学者头痛,尤其是需要多次操作的时候,GLib中提供了类似的函数g_malloc和g_free,最好用的方法是其将g_malloc函数封装成了宏g_new,这个宏有两个参数,第一个是结构类型,第二个是要分配结构的数量,这段代码中只用到了一个Arg数据结构,所以是g_new(Arg,
1)。在程序结束时用g_free来释放。
在线程初始化时,首先是判断线程是否初始化的函数g_thread_supported,如果其返回FALSE则表明线程并未初始化,这时必须用g_thread_init来初始化,这是较明智的做法。
事件循环GMainLoop在用g_main_loop_new创建之后并不马上运行,用g_main_loop_run运行后,还要用g_main_loop_quit退出,否则循环将一直运行下去,这两个函数的参数都是GMainLoop型的指针,在主函数中并未直接运行g_main_loop_quit,而是把它放在线程的函数中了,这一点需读者注意。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: