GLib需要一個支持線程的操作系統(tǒng)和一個字符集間轉(zhuǎn)換函數(shù)iconv的支持,事實上大多現(xiàn)代的操作系統(tǒng)都有以上兩項功能。 GLib由基礎(chǔ)類型、對核心應(yīng)用的支持、實用功能、數(shù)據(jù)類型和對象系統(tǒng)五個部分組成的。 GLib的最新版本是GLib2.2.1,可以到www.gtk.org網(wǎng)站下載其源代碼。使用GLib2.0編寫的應(yīng)用程序,在編譯時應(yīng)該在編譯命令中加入`pkg-config -cflags -libs glib-2.0`,如編譯一個名為hello.c的程序,輸出名為hello的可執(zhí)行文件,則命令為: gcc `pkg-config -cflags -libs glib-2.0` hello.c -o hello 在GLIB中將線程(gthread),插件(gmoudle)和對象系統(tǒng)(gobject)這三個子系統(tǒng)區(qū)別對待,編譯時要注意加入相應(yīng)的參數(shù)。 如程序中用到對象系統(tǒng),編譯時就應(yīng)加入: `pkg-config --cflags --libs gobject-2.0` 用到線程,編譯時則加入: `pkg-config --cflags --libs gthread-2.0` 用到插件,編譯時則加入: `pkg-config --cflags --libs gmoudle-2.0` 基礎(chǔ)類型GLib的基礎(chǔ)是由基礎(chǔ)類型、范圍限定宏、標準宏、類型轉(zhuǎn)換宏、字節(jié)次序變換宏、數(shù)學常數(shù)定義和雜項宏等各項組成的。這里主要介紹基礎(chǔ)類型,因為它們遍及與GLIB相關(guān)的各種程序庫和軟件包中,如GTK+,GNOME,MONO等大的開源項目。 基礎(chǔ)類型又稱標準類型,GLib將C語言中的數(shù)據(jù)類型統(tǒng)一封裝成自己的數(shù)據(jù)類型,均以小寫字母'g'開頭,如:gpointer是指針類型(void *)、guint是無符號整型(unsigned int)等,其中有一些是修飾性的,如:gint、gchar等,它們和C語言中的int、char是完全相同的。這些數(shù)據(jù)類型使用起來和C語言中的數(shù)據(jù)類型完全一樣,當你熟悉了以后會發(fā)現(xiàn)它們的使用方法更靈活,更直觀也更易于理解一些。當然你可以把C語言中的數(shù)據(jù)類型直接拿來使用,這絲毫不影響你編寫程序的編譯。 另外范圍限定宏和類型轉(zhuǎn)換宏也較常用,如G_MAXINT表示最大的int型值,用宏GINT_TO_POINTER(i)將整型變量i轉(zhuǎn)換為指針型,宏GPOINTER_TO_INT(p)將指針型變量p轉(zhuǎn)換為整型。 邏輯類型gboolean的值TRUE和FALSE是在常數(shù)宏中定義的,另外還包括G_E表示自然對數(shù),G_PI表示圓周率,G_PI_2表示圓周率的1/2等一些數(shù)學常數(shù)。 對核心應(yīng)用的支持GLib對核心應(yīng)用的支持包括事件循環(huán)、內(nèi)存操作、線程操作、動態(tài)鏈接庫的操作和出錯處理與日志等。 下面代碼演示了事件循環(huán)、內(nèi)存操作、線程這三種功能的簡單應(yīng)用: #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退出事件循環(huán)\n"); g_mutex_free(mutex); g_print("釋放互斥對象\n"); g_free(arg); g_print("釋放參數(shù)所用的內(nèi)存\n"); } Makefile文件如下: CC = gcc all: $(CC) `pkg-config --cflags --libs glib-2.0 gthread-2.0` loop.c -o loop 下面為輸出結(jié)果: 0 :線程1鎖定了互斥對象
以上例程創(chuàng)建了三個線程,其中run_1和run_2操作互斥對象,run_3檢索前兩個線程是否結(jié)束,如結(jié)束的話,則執(zhí)行g(shù)_main_loop_quit退出事件循環(huán)。由于線程的運行是不確定的,所以不一定每次都是這一輸出結(jié)果。 首先定義一個結(jié)構(gòu)類型來保存創(chuàng)建的事件循環(huán)的對象指針和線程運行時的最多循環(huán)次數(shù),一般情況下,如果為此數(shù)據(jù)結(jié)構(gòu)來分配內(nèi)存的話,用Arg *arg = (Arg *)malloc(sizeof(Arg));,釋放時用free(arg);,這種傳統(tǒng)的做法曾經(jīng)讓很多C語言的初學者頭痛,尤其是需要多次操作的時候,GLib中提供了類似的函數(shù)g_malloc和g_free,最好用的方法是其將g_malloc函數(shù)封裝成了宏g_new,這個宏有兩個參數(shù),第一個是結(jié)構(gòu)類型,第二個是要分配結(jié)構(gòu)的數(shù)量,這段代碼中只用到了一個Arg數(shù)據(jù)結(jié)構(gòu),所以是g_new(Arg, 1)。在程序結(jié)束時用g_free來釋放。 在線程初始化時,首先是判斷線程是否初始化的函數(shù)g_thread_supported,如果其返回FALSE則表明線程并未初始化,這時必須用g_thread_init來初始化,這是較明智的做法。 事件循環(huán)GMainLoop在用g_main_loop_new創(chuàng)建之后并不馬上運行,用g_main_loop_run運行后,還要用g_main_loop_quit退出,否則循環(huán)將一直運行下去,這兩個函數(shù)的參數(shù)都是GMainLoop型的指針,在主函數(shù)中并未直接運行g(shù)_main_loop_quit,而是把它放在線程的函數(shù)中了,這一點需讀者注意。 實用功能GLib中包含了近二十種實用功能,從簡單的字符處理到初學者很難理解的XML解析功能,這里介紹兩種較簡單的:隨機數(shù)和計時。 下面代碼演示如何產(chǎn)生1-100之間的隨機整數(shù)和演示如何計算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(); //創(chuàng)建隨機數(shù)對象 for(n=0; n<20; n++) { //產(chǎn)生隨機數(shù)并顯示出來 g_print("%d\t",g_rand_int_range(rand,1,100)); } g_print("\n"); g_rand_free(rand); //釋放隨機數(shù)對象 //創(chuàng)建計時器 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);//計時結(jié)束 //輸出計時結(jié)果 g_print("%ld\tall:%.2f seconds was used!\n",x,g_timer_elapsed(timer,NULL)); } Makefile文件內(nèi)容如下: CC = gcc all: $(CC) `pkg-config --cflags --libs glib-2.0 ` until.c -o until 輸出結(jié)果: 48 95 95 99 90 24 90 29 78 4 53 87 1 86 7 93 57 88 75 4 30000000 all:1.47 seconds was used! GLIB中的每個對象幾乎都有一個或多個*_new函數(shù)來創(chuàng)建,計時器GTimer和隨機器GRand也一樣,也都有相對應(yīng)的函數(shù)來結(jié)束對象的使用,如GTimer的g_timer_stop和GRand的g_rand_free。 這可能是GLIB實用功能中最簡單的兩種了,許多朋友會一目了然。我們還應(yīng)注意到GLIB的代碼風格和封裝技巧是具有獨到之處的,這種風格和技巧足以讓一些自稱簡潔實用的SDK汗顏,學習掌握這一風格可能會讓我們受益匪淺。 數(shù)據(jù)類型GLib中定義了十幾種常用的數(shù)據(jù)結(jié)構(gòu)類型和它們的相關(guān)操作函數(shù),下面是關(guān)于字符串類型的簡單示例: #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 下面是輸出結(jié)果: Hello Hello World! World! Also a World! Also a Nice World! 字符串在編程中出現(xiàn)頻率之高,即使是初學者也很清楚,追加、刪除和插入等常用操作理解后,還可以進一步了解掌握其它更復雜的操作。 GLib提供了一種內(nèi)存塊(GMemChunk)數(shù)據(jù)類型,它為分配等大的內(nèi)存區(qū)提供了一種非常好用的操作方式,下面程序演示了內(nèi)存塊數(shù)據(jù)類型的簡單用法: #include <glib.h> int main(int argc, char *argv[]) { GMemChunk *chunk; //定義內(nèi)存塊 gchar *mem[10]; //定義指向原子的指針數(shù)組 gint i, j; //創(chuàng)建內(nèi)存塊 chunk = g_mem_chunk_new("Test MemChunk", 5, 50, G_ALLOC_AND_FREE); //名稱,原子的長度, 內(nèi)存塊的長度,類型 for(i=0; i<10; i++) { //創(chuàng)建對象 //mem[i] = g_chunk_new(gchar, chunk); mem[i] = (gchar*)g_mem_chunk_alloc(chunk); for(j=0; j<5; j++) { mem[i][j] = 'A' + j;//為內(nèi)存塊中的指針賦值 } } g_mem_chunk_print(chunk); //顯示內(nèi)存塊信息 for(i=0; i<10; i++) { g_print("%s\t",mem[i]);//顯示內(nèi)存塊中的內(nèi)容 } for(i=0; i<10; i++) { g_mem_chunk_free(chunk,mem[i]); //釋放所有分配的內(nèi)存 } g_mem_chunk_destroy(chunk); } Makefile文件件如下: CC = gcc all: $(CC) `pkg-config --cflags --libs glib-2.0` data1.c -o data1 以下為輸出結(jié)果: GLib-INFO: Test MemChunk: 80 bytes using 2 mem areas ABCDE ABCDE ABCDE ABCDE ABCDE ABCDE ABCDE ABCDE ABCDE ABCDE 這里說明這一數(shù)據(jù)類型的原因是通過它可以他細體會內(nèi)存分配這一運行時處理環(huán)節(jié)的應(yīng)用之妙。 我們在程序中分配的是50字節(jié)的空間,而實際用的是80字節(jié),由此可以看出其在分配內(nèi)存時本身用到了部分內(nèi)存空間。 從上面的示例代碼中可以看出,在GLib中幾乎所有的對象都是C語言的結(jié)構(gòu)類型,一般命名以大寫字母G開頭的單詞,如GList表示雙向鏈表,所有與之相關(guān)的操作函數(shù)都以小寫的字母g加下劃線加小寫的單詞加下劃線開頭,如以g_list_*開頭的函數(shù)都是與這相關(guān)的操作函數(shù),而且這些函數(shù)中的第一個參數(shù)多數(shù)是此對象的指針。 GLIB中的數(shù)據(jù)類型在GLIB本身,尤其是GTK+中頻繁用到,了解掌握這些數(shù)據(jù)類據(jù)類型的用法是非常必要的,這對進一步靈活開發(fā)GTK+程序來說是關(guān)鍵一環(huán),而且是對大學中的《數(shù)據(jù)結(jié)構(gòu)》一科的很好回顧。 在下一篇GOBJECT對象系統(tǒng)中將詳細介紹GLIB中最重要的組成部分GOBJECT系統(tǒng),希望這一由C語言組建的單繼承的對象系統(tǒng)會幫助你走進GTK+的世界。 |
|