天天看點

從C 語言使用者角度了解 Linux 的庫

  從軟體誕生的那天開始,如何有效地重複利用代碼就成為了軟體的開發的需要,否則,每個程式都要從頭開始,程式設計就談不上樂趣,就無法大規模推廣了。C語言中庫無疑是解決代碼重複利用的重要途徑之一,是非常簡單合理的複用代碼的一種方式。其實,軟體庫技術:長期存在;簡單合理; 代碼複用。下面以 Linux 為例,建立、釋出和使用這些庫,這些步驟也可以應用于其它類 Unix 系統。這些示例庫使用 C 語言編寫,适合該任務。Linux 核心大部分由 C 語言和少量彙編語言編寫(Windows 和 Linux 的表親如 macOS 也是如此)。用于輸入/輸出、網絡、字元串處理、數學、安全、資料編碼等的标準系統庫等主要由 C 語言編寫。是以使用 C 語言編寫庫就是使用 Linux 的原生語言來編寫。除此之外,C 語言的性能也非常突出的。主要包括庫和測試client程式用 C 語言客戶程式來通路 C 語言編寫的庫,實際也可以用于其他程式設計語言。

一、靜态庫和動态庫  

linux中主要有靜态庫和動态庫兩種。其中

  靜态庫:連結時直接将庫檔案的内容編碼進代碼;每個用到庫檔案的client程式都擁有庫檔案的拷貝;如果庫檔案更新了須要再次更新連結;

  靜态庫:連結階段隻是做了辨別;多個client用到的同一個動态庫檔案在記憶體中隻有一份拷貝;庫檔案的更新須要系統加載器将共享庫和client程式連結即可;動态庫性能不如靜态庫高效;動态庫的複雜性高;動态庫的應用異常靈活;

  1、庫的源碼先被便衣成一個或多個目标子產品(二進制檔案,但不包含可執行資訊),目标子產品可以被包含到庫中;也可以被連結到可執行的二進制檔案中

  2、目标子產品被打包成一個檔案(靜态庫以.a動态庫以.o)特殊字尾名,一般加上lib字首

  3、庫檔案的目錄:/usr/lib   /usr/local/lib  ./ 目前目錄

二、C語言的函數

  1、函數的存儲類決定着函數的應用範圍,其中extern(預設)表明函數是整個庫有效的;而static表明函數的範圍被限制到函數所在的檔案中;  

   通過函數的存儲類型的修飾符,完成函數應用範圍的區分與特定函數的隐藏與隔離

  2、函數的定義和聲明,C語言中隻允許命名函數

  函數定義:1)、函數名稱必須唯一

       2)、參數必須指明類型,參數清單可以為空,也就是無需傳入參數

         3)、傳回必須有具體類型,若明确無傳回,需指定為void,隻能傳回一個值

         4)、函數主體包含若幹程式語句,須要用{}包括,

  3、函數的聲明,與函數定義唯一的不同就是函數主體部分用 ; 替換{}部分即可;函數可以被多次聲明,但隻能被定義一次

  4、函數的應用,

        1)、C語言中規定變量需先定義後使用,

        2)、被調用函數需對調用函數是可見的,

        3)、對一些暫時沒有實作的被調用的函數,可以通過先聲明,後定義的方式避免程式設計過程被打斷、

        4)、也可以通過先定義被調用函數,再調用;

  5、庫

        1)、C語言中功能相近的函數形成庫,

        2)、庫中的函數通過其頭檔案實作對調用的函數的隐藏和封裝,

        3)、頭檔案是客戶程式與庫函數的接口,

        4)、動态庫的優勢會更加明顯

三、靜态庫的三步走制作:

  以源代碼primes.c為例,目标檔案和庫檔案均以正常方式命名。

  1、生成目标檔案

  gcc -c  primes.c                 //隻編譯,生成目标檔案primes.o

  2、ar  -cvq  libprimes.a  primesc.o        //ar是靜态庫壓縮的指令;選項cvq是建立、詳細和快速添加等;庫名須以lib為字首,靜态庫以.a為字尾名,庫名必須是唯一的;

  3、sudo  cp  libprimes.a  /usr/local/lib       //靜态庫的釋出,就是将其拷貝至相應的目錄;

四、動态庫的五步驟制作:

  1、生成位置無關檔案

  gcc  primes.c  -c  -fpic              //編譯選項fpic,生成位置無關代碼

  2、gcc  -shared  -W1, -soname,   libshprimes.so  -o  libshprimes.so.1  primes.o //gcc是動态庫制作的指令;

                         //選項shared表明庫是共享(動态)的,而非靜态的;

                         //選項soname指定了庫的邏輯名稱;客戶程式一般通過庫的邏輯名稱通路庫檔案

                           //選項o指定了庫的實體檔案名稱;

  3、sudo  cp  libshprimes.so.1  /usr/local/lib     //動态庫的釋出,就是将其拷貝至相應的目錄;

  4、sudo  ln --symbolic  libshprimes.so.1  libshprimes.so //連結庫的實體名稱和邏輯名稱

  5、sudo  ldconfig                //使用ldconfig配置系統的動态加載器,此過程是為了確定系統的中的庫加載器能夠正确找到新釋出的庫

五、實戰

  1、primes.h

/** header file primes.h: function declarations **/
extern unsigned is_prime(unsigned);
extern void prime_factors(unsigned);
extern unsigned are_coprimes(unsigned, unsigned);
extern void goldbach(unsigned);      

  2、primes.c

#include <stdio.h>
#include <math.h>

extern unsigned is_prime(unsigned n) { 
  if (n <= 3) return n > 1;                   /* 2 and 3 are prime */
  if (0 == (n % 2) || 0 == (n % 3)) return 0; /* multiples of 2 or 3 aren't */

  /* check that n is not a multiple of other values < n */
  unsigned i;
  for (i = 5; (i * i) <= n; i += 6)
    if (0 == (n % i) || 0 == (n % (i + 2))) return 0; /* not prime */

  return 1; /* a prime other than 2 or 3 */
}

extern void prime_factors(unsigned n) {
  /* list 2s in n's prime factorization */
  while (0 == (n % 2)) {  
    printf("%i ", 2);
    n /= 2;
  }

  /* 2s are done, the divisor is now odd */
  unsigned i;
  for (i = 3; i <= sqrt(n); i += 2) {
    while (0 == (n % i)) {
      printf("%i ", i);
      n /= i;
    }
  }

  /* one more prime factor? */
  if (n > 2) printf("%i", n);
}

/* utility function: greatest common divisor */
static unsigned gcd(unsigned n1, unsigned n2) {
  while (n1 != 0) {
    unsigned n3 = n1;
    n1 = n2 % n1;
    n2 = n3;
  }
  return n2;
}

extern unsigned are_coprimes(unsigned n1, unsigned n2) {
  return 1 == gcd(n1, n2);
}

extern void goldbach(unsigned n) {
  /* input errors */
  if ((n <= 2) || ((n & 0x01) > 0)) {
    printf("Number must be > 2 and even: %i is not.\n", n);
    return;
  }

  /* two simple cases: 4 and 6 */
  if ((4 == n) || (6 == n)) {
    printf("%i = %i + %i\n", n, n / 2, n / 2);
    return;
  }
  
  /* for n >= 8: multiple possibilities for many */
  unsigned i;
  for (i = 3; i < (n / 2); i++) {
    if (is_prime(i) && is_prime(n - i)) {
      printf("%i = %i + %i\n", n, i, n - i);
      /* if one pair is enough, replace this with a break */
    }
  }
}      

  3、測試檔案,testPrimes.c

#include <stdio.h>
#include <primes.h>

int main() {
  /* is_prime */
  printf("\nis_prime\n");
  unsigned i, count = 0, n = 1000; 
  for (i = 1; i <= n; i++) {
    if (is_prime(i)) {
      count++;
      if (1 == (i % 100)) printf("Sample prime ending in 1: %i\n", i);
    }
  }
  printf("%i primes in range of 1 to a thousand.\n", count);

  /* prime_factors */
  printf("\nprime_factors\n");
  printf("prime factors of 12: ");
  prime_factors(12);
  printf("\n");
  
  printf("prime factors of 13: ");
  prime_factors(13);
  printf("\n");
  
  printf("prime factors of 876,512,779: ");
  prime_factors(876512779);
  printf("\n");

  /* are_coprimes */
  printf("\nare_coprime\n");
  printf("Are %i and %i coprime? %s\n",
     21, 22, are_coprimes(21, 22) ? "yes" : "no");
  printf("Are %i and %i coprime? %s\n",
     21, 24, are_coprimes(21, 24) ? "yes" : "no");

  /* goldbach */
  printf("\ngoldbach\n");
  goldbach(11);    /* error */
  goldbach(4);     /* small one */
  goldbach(6);     /* another */
  for (i = 100; i <= 150; i += 2) goldbach(i); 

  return 0;
}      

或者:

from ctypes import cdll

cdll.LoadLibrary("libshprimes.so") ## logical name
primes.is_prime(13)
primes.is_prime(12)

primes.are_coprimes(8, 24)
primes.are_coprimes(8, 25)

primes.prime_factors.restype = None
primes.goldbach.restype = None

primes.prime_factors(72)
primes.goldbach(32)      

人就像是被蒙着眼推磨的驢子,生活就像一條鞭子;當鞭子抽到你背上時,你就隻能一直往前走,雖然連你也不知道要走到什麼時候為止,便一直這麼堅持着。

繼續閱讀