C 語言與 glibc 函式庫

GNU 程式設計

GNU 程式簡介

GNU 與 C 語言

gcc 編譯器

glibc 函式庫

make 建置

ld 連結器

as 組譯器

ar 函式庫

objdump

objcopy

訊息

相關網站

參考文獻

最新修改

簡體版

English

目前,在許多資訊與電子相關科系中,都是以 C 語言做為第一個程式語言,但是,C 語言絕對不是簡單的語言。相反的,C語言當中有很多陷阱,會令初學者百思不得其解。即便是C語言的專家,在使用這個語言時,都要敬畏三分。因為,C語言是以效率為首要考量,而非學習的容易程度。雖然,筆者已經學過 Pascal, C, VB, Foxpro, Java, C#, LISP, Prolog, Ruby, Python, ASP 等語言,但是,C語言仍是我所學習過的語言當中最難學,也是最容易出錯的一個語言。

舉例而言,C語言中的指標經常會造成錯誤百出的狀況,但由於指標是C語言強而有力的武器,在低階輸出入、效能表現、與靈活度上都有很強的用途,因此,指標可以說是一把鋒利的雙面刃,若不砍死敵人就會砍傷自己。

C語言中的指標

在C語言當中,宣告指標的情況很常見,C語言中的各種型態都可以加上星號 (*) 後就成為指標型態,而且,指標可以指向任何的記憶體區塊,舉例而言,下列程式可以將 'a' 字元塞入到記憶體位址 100 當中,這種方式常被用來存取記譯體映射輸出入的暫存器。

char *ptr=(char*) 100;
*ptr = 'a'

函數指標是C語言中相當常用到的功能之一,但是這個主題卻有相當的難度。利用函式指標可以進行反向呼叫 (Call Back Function),舉例而言,在標準函式庫中的 qsort 函數,就使用了 int (*compare)(const void *, const void *) 這樣一個函數指標。

為了效率的考量,C語言的函式庫通常採用靜態的配置方式,盡可能避免動態配置記憶體,因此像 strcpy(), sscanf() 等函數都不會動態配置記憶體,這樣可以讓函數使用者自行決定是否要自行配置方式。但是,當使用者決定以靜態的方式宣告陣列或字串大小,而不採用動態配置時,通常會很難決定陣列或字串所需的空間大小。因此,撰寫 C 語言的程式設計人員對記憶體的掌握度較高,但是也相對必須花費許多程式碼在記憶體管理上。

C語言由於設計上非常注重效率問題,而且與組合語言之間的連接相當良好,因此,很早就成為了系統程式的主流語言。但是,由於C語言的歷史淵遠流長,而且早已被廣泛使用在各個作業系統的撰寫上,這也使得C語言的函式庫相當的不統一。

雖然,ISO組織定義了 C 語言的標準函式庫,但是,這些函式庫有時會不足以應付許多實務上的應用,因此,各家廠商經常會自行發展其C語言函式庫,舉例而言,微軟平台的C語言函式庫與GNU的C語言函式庫就相當不同。更嚴重的是,由於各家作業系統幾乎都是以 C/C++所開發的,因此,各自擁有不同的系統函式庫,這對C語言的開發人員而言,造成了一些困擾,因為,要學習作業系統上的程式設計,就必須分別學習各個作業系統不同的函式庫。

ISO組織定義的C語言函式庫,是以函數為主的定義方式,但是,卻缺乏標準的資料結構,像是 HashTable, Binary Tree 等,這使得C語言的開發者常常被迫要重寫這些結構的相關函數,還好,在開放原始碼領域,GNU已經為我們製作了一些廣泛被使用的函式庫,稱為 GNU C library (glibc) 。

除了包含 ISO C 函式庫之外,glibc 還包含 POSIX (Portable Operating System Interface for Unix) 所定義的函數,POSIX 是 UNIX作業系統家族的標準介面,UNIX, Linux, Cygwin 等環境都支援 POSIX 標準,因此,glibc 的函數大部分都可以在這些平台上執行。

除此之外,glibc 還包含了一些其他平台中常見的好用函數,像是 Berkley Unix (BSD and SunOS) 的函數,UNIX System V Interface Description (SVID) 的函數,與 The X/Open Portability Guide 中的函數等等。表格 7.5顯示了 glibc 的標頭檔列表,僅供讀者參考。

表格 7.5 glibc函式庫的標頭檔分類表

ISO標準 說明 POSIX 標準 說明 其他檔頭 說明
assert.h 錯誤偵測 cpio.h 壓縮格式 argz.h 參數取得
complex.h 數學複數 dirent.h 目錄操作 envz.h 參數取得
ctype.h 字元處理 fcntl.h 檔案操作 execinfo.h 除錯堆疊
errno.h 錯誤處理 grp.h 群組管理 fnmatch.h 字串比對
fenv.h 浮點環境 pwd.h 帳號密碼 gconv.h 國際轉換
float.h 浮點數 sys/ipc.h 行程通訊 langinfo.h 格式轉換
inttypes.h 整數轉換 sys/msg.h 訊息佇列 mcheck.h 記憶體檢查
iso646.h 運算詞彙 sys/sem.h 號誌 ulimit.h 資源限制
limits.h 數值範圍 sys/stat.h 檔案資訊 utmp.h 使用者帳號
locale.h 國際化 sys/time.h 日期時間 obstack.h 物件堆疊
math.h 數學函數 sys/types.h 型態定義 printf.h 參數剖析
setjmp.h 遠程跳躍 sys/utsname.h 平台型號 regex.h 正規表示式
signal.h 引發錯誤 sys/wait.h 行程等待 search.h 排序搜尋
stdarg.h 變動參數 tar.h 壓縮格式 sys/param.h 系統參數
stdbool.h 布林型態 termios.h 終端機 sys/resource.h 系統資源
stddef.h 通用定義 unistd.h UNIX sys/stat.h 系統設定
stdint.h 整數型態 utime.h 檔案時間 sys/time.h 系統時間
stdio.h 輸出入 sys/types.h 系統型態
stdlib.h 一般函數 sys/utsname.h 系統名稱
string.h 字串處理 sys/vlimit.h 系統限制
tgmath.h 數學函數 sys/vtimes.h 系統時間
time.h 日期時間 sys/wait.h 行程等待
wchar.h Unicode sys/socket.h 網路函數
wctype.h Unicode netdb.h 網路資料
netinet/in.h 網路輸入
sys/un.h 網址

在glibc當中,補充了標準函式庫的一些不足之處,這包含二元樹 (Binary Tree)、赫序函數 (HashTable)、Unicode 字元處理函數、正規表示式、網路函式庫(Socket)、以及許多作業系統的相關函式庫等等,在本節中,我們將介紹glibc函式庫的一些常見用法,這些背景知識對於我們實作系統軟體時會有所幫助。

glibc 的二元樹與HashTable之使用方式

檔案:search.c                                             // 說明

#include <search.h>                                        // 引用檔案 search.h
#include <stdlib.h>                                        
#include <stdio.h>                                         

char *names[] = { "George", "Mary",                        // 宣告名單
"Bob", "Snoopy", "Mickey", "John", "Mike" };               

typedef struct {                                           // 宣告結構 person
  char *name;                                               
  int  id;                                                   
} person;                                                   

#define person_print(p) \                                  // 定義輸出函數
printf("id=%d name=%s\n", p->id, p->name)                   

int compare(const void *pa, const void *pb) {              // 定義比較函數
  person *p1=(person*)pa, *p2=(person*)pb;                   
  return strcmp(p1->name, p2->name);                       
}                                                           

void action(const void *nodep, const VISIT which,          // 定義節點訪問動作
            const int depth) {                               
  person *p;                                               
  switch(which) {                                           
    case preorder:                                           
      break;                                               
    case postorder:                                           
      p = *(person**)nodep;                                   
      person_print(p);                                       
      break;                                               
    case endorder:                                           
      break;                                               
    case leaf:                                               
      p = *(person**)nodep;                                   
      person_print(p);                                       
      break;                                               
  }                                                           
}                                                           

void binaryTreeTest() {                                    // 二元樹測試程式
  void *v, *root=NULL;                                     // 宣告根節點
  int i;                                                   
  for (i = 0; i < 7; i++) {                                // 將名單加入樹中
    person *p = (person*) malloc(sizeof(person));          // 分配空間
    p->name = strdup(names[i]);                            // 複製名稱
    p->id = i;                                             // 設定編號
    v = tsearch((void *)p, &root, compare);                // 新增p記錄
  }                                                           
  twalk(root, action);                                     // 列印整顆樹
}                                                           

void hashTableTest() {                                     // HashTable 測試程式
  void *v;                                                   
  int i;                                                   
  ENTRY e, *ep;                                               

  hcreate(30);                                             // 建立 hashTable
  for (i = 0; i < 5; i++) {                                // 將名單放入表格
    person *p = (person*) malloc(sizeof(person));          // 分配空間
    p->name = strdup(names[i]);                            // 複製名稱
    p->id = i;                                             // 設定編號
    e.key = p->name;                                       // 設定e=(key, data)
    e.data = p;                                               
    v = hsearch(e, ENTER);                                 // 新增e記錄
  }                                                           
  for (i=0; i<7; i++) {                                    // 從表格中取出名單
    e.key = names[i];                                      // 設定搜尋的 key
    ep = hsearch(e, FIND);                                 // 開始搜尋
    if (ep != NULL) {                                      // 如果有找到
      person *p = ep->data;                                //   取得 person
      person_print(p);                                     //   列印 person
    } else                                                 // 否則
      printf("%s not found !\n", e.key);                   //   印出找不到
  }                                                           
  hdestroy();                                              // 刪除 HashTable
}                                                           

int main() {                                               // 主程式
  printf("=====binaryTreeTest()=======\n");                   
  binaryTreeTest();                                        // 測試二元樹
  printf("=====hashTableTest()=======\n");                   
  hashTableTest();                                         // 測試 HashTable
  return 0;
}

執行過程與結果:

$ gcc search.c -o search

$ ./search
=====binaryTreeTest()=======
id=2 name=Bob
id=0 name=George
id=5 name=John
id=1 name=Mary
id=4 name=Mickey
id=6 name=Mike
id=3 name=Snoopy
=====hashTableTest()=======
id=0 name=George
id=1 name=Mary
id=2 name=Bob
id=3 name=Snoopy
id=4 name=Mickey
John not found !
Mike not found !

Facebook

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License