浅析GLib

GLib是GTK+和GNOME工程的基础底层核心程序库,是一个综合用途的实用的轻量级的C程序库,它提供C语言的常用的数据结构的定义、相关的处理函数,有趣而实用的宏,可移植的封装和一些运行时机能,如事件循环、线程、动态调用、对象系统等的API。它能够在类UNIX的操作系统平台(如LINUX, HP-UNIX等),WINDOWS,OS2和BeOS等操作系统台上运行


GLib需要一个支持线程的操作系统和一个字符集间转换函数iconv的支持,事实上大多现代的操作系统都有以上两项功能。

GLib由基础类型、对核心应用的支持、实用功能、数据类型和对象系统五个部分组成的。

使用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`

基础类型

GLib的基础是由基础类型、范围限定宏、标准宏、类型转换宏、字节次序变换宏、数学常数定义和杂项宏等各项组成的。这里主要介绍基础类型,因为它们遍及与GLIB相关的各种程序库和软件包中,如GTK+,GNOME,MONO等大的开源项目。

基础类型又称标准类型,GLib将C语言中的数据类型统一封装成自己的数据类型,均以小写字母'g'开头,如:gpointer是指针类型(void *)、guint是无符号整型(unsigned int)等,其中有一些是修饰性的,如:gint、gchar等,它们和C语言中的int、char是完全相同的。这些数据类型使用起来和C语言中的数据类型完全一样,当你熟悉了以后会发现它们的使用方法更灵活,更直观也更易于理解一些。当然你可以把C语言中的数据类型直接拿来使用,这丝毫不影响你编写程序的编译。

另外范围限定宏和类型转换宏也较常用,如G_MAXINT表示最大的int型值,用宏GINT_TO_POINTER(i)将整型变量i转换为指针型,宏GPOINTER_TO_INT(p)将指针型变量p转换为整型。

逻辑类型gboolean的值TRUE和FALSE是在常数宏中定义的,另外还包括G_E表示自然对数,G_PI表示圆周率,G_PI_2表示圆周率的1/2等一些数学常数。

对核心应用的支持

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 :线程2锁定了互斥对象\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 :线程1锁定了互斥对象\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("线程3退出事件循环\n");
	g_mutex_free(mutex);
	g_print("释放互斥对象\n"); 
	g_free(arg);
	g_print("释放参数所用的内存\n");
}

Makefile文件如下:

CC = gcc
all:
	$(CC) `pkg-config --cflags --libs glib-2.0 gthread-2.0` loop.c -o loop

以上例程创建了三个线程,其中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,而是把它放在线程的函数中了,这一点需读者注意。

实用功能

GLib中包含了近二十种实用功能,从简单的字符处理到初学者很难理解的XML解析功能,这里介绍两种较简单的:随机数和计时。

下面代码演示如何产生1-100之间的随机整数和演示如何计算30000000次累加在计算时用的时间:

/* until.c 用来测试实用功能 */
#include <glib.h>
int	main(int argc, char *argv[])
{
	GRand *rand;
	GTimer *timer;
	
	gint n;
	gint i, j;
	gint x = 0;
	rand = g_rand_new();	//创建随机数对象
	for(n=0; n<20; n++)
	{	//产生随机数并显示出来
		g_print("%d\t",g_rand_int_range(rand,1,100));
	}
	g_print("\n");
	g_rand_free(rand);	//释放随机数对象
	//创建计时器
	timer = g_timer_new();
	g_timer_start(timer);//开始计时
	for(i=0; i<10000; i++)
		for(j=0; j<3000; j++)
			x++;//累计
	g_timer_stop(timer);//计时结束
	//输出计时结果
	g_print("%ld\tall:%.2f seconds was used!\n",x,g_timer_elapsed(timer,NULL));
}

Makefile文件内容如下:

CC = gccall:$(CC) `pkg-config --cflags --libs glib-2.0 ` until.c -o until

GLIB中的每个对象几乎都有一个或多个*_new函数来创建,计时器GTimer和随机器GRand也一样,也都有相对应的函数来结束对象的使用,如GTimer的g_timer_stop和GRand的g_rand_free。

这可能是GLIB实用功能中最简单的两种了,许多朋友会一目了然。我们还应注意到GLIB的代码风格和封装技巧是具有独到之处的,这种风格和技巧足以让一些自称简洁实用的SDK汗颜,学习掌握这一风格可能会让我们受益匪浅。

数据类型

GLib中定义了十几种常用的数据结构类型和它们的相关操作函数,下面是关于字符串类型的简单示例:

#include <glib.h>
int	main(int argc, char *argv[])
{
	GString *s;
	s = g_string_new("Hello");
	g_print("%s\n", s->str);
	s = g_string_append(s," World!");
	g_print("%s\n",s->str);
	s = g_string_erase(s,0,6);
	g_print("%s\n",s->str);
	s = g_string_prepend(s,"Also a ");
	g_print("%s\n",s->str);
	
	s = g_string_insert(s,6," Nice");
	g_print("%s\n",s->str);
}
Makefile文件如下:
CC = gcc
all:
	$(CC) `pkg-config --cflags --libs glib-2.0 ` string.c -o str




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值