C編譯: 動態串連庫 (.so檔案)(轉摘)

來源:互聯網
上載者:User

標籤:

Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉載,也請保留這段聲明。謝謝!

 

在“紙上談兵: 演算法與資料結構”中,我在每一篇都會有一個C程式,用於實現演算法和資料結構 (比如棧和相關的操作)。在同一個程式中,還有用於測試的main()函數,結構體定義,函數原型,typedef等等。

這樣的做法非常不“環保”。演算法的實際運用和演算法的實現混在一起。如果我想要重複使用之前的來源程式,必須進行許多改動,並且重新編譯。最好的解決方案是實現模組化: 只保留純粹的演算法實現,分離標頭檔,並編譯一個庫(library)。每次需要使用庫的時候(比如使用棧資料結構),就在程式中include標頭檔,串連庫。這樣,不需要每次都改動來源程式。

我在這裡介紹如何在UNIX環境中建立共用庫 (shared library)。UNIX下,共用庫以so為尾碼(shared object)。共用庫與Windows下的DLL類似,是在程式運行時動態串連。多個進程可以串連同一個共用庫。

 

共用庫

本文使用Ubuntu測試,使用gcc作為編譯器。

 

程式清理

下面程式來自紙上談兵: 棧 (stack),是棧資料結構的C實現:

/* By Vamei *//* use single-linked list to implement stack */#include <stdio.h>#include <stdlib.h>typedef struct node *position;typedef int ElementTP;// point to the  head node of the listtypedef struct node *STACK; struct node {    ElementTP element;    position next;};STACK init_stack(void);void delete_stack(STACK);ElementTP top(STACK);void push(STACK, ElementTP);ElementTP pop(STACK);int is_null(STACK);void main(void){    ElementTP a;    int i;    STACK sk;    sk = init_stack();    push(sk, 1);    push(sk, 2);    push(sk, 8);    printf("Stack is null? %d\n", is_null(sk));    for (i=0; i<3; i++) {        a = pop(sk);        printf("pop: %d\n", a);    }    printf("Stack is null? %d\n", is_null(sk));        delete_stack(sk);}/* * initiate the stack * malloc the head node. * Head node doesn‘t store valid data * head->next is the top node */STACK init_stack(void){    position np;    STACK    sk;    np = (position) malloc(sizeof(struct node));    np->next     = NULL;  // sk->next is the top node    sk = np;     return sk;}/* pop out all elements  * and then delete head node */void delete_stack(STACK sk){    while(!is_null(sk)) {        pop(sk);    }    free(sk);}/*  * View the top frame */ElementTP top(STACK sk){    return (sk->next->element);}/* * push a value into the stack */void push(STACK sk, ElementTP value) {    position np, oldTop;    oldTop = sk->next;        np = (position) malloc(sizeof(struct node));    np->element  = value;    np->next     = sk->next;    sk->next     = np; }/*  * pop out the top value */ElementTP pop(STACK sk){    ElementTP element;    position top, newTop;    if (is_null(sk)) {        printf("pop() on an empty stack");        exit(1);    }     else {        top      = sk->next;        element  = top->element;             newTop   = top->next;        sk->next     = newTop;        free(top);        return element;    } }/* check whether a stack is empty*/int is_null(STACK sk){    return (sk->next == NULL);}

上面的main()部分是用於測試,不屬於功能模組,在建立庫的時候應該去掉。

 

程式中的一些聲明,會被重複利用。比如:

typedef struct node *position;typedef int ElementTP;// point to the  head node of the listtypedef struct node *STACK; struct node {    ElementTP element;    position next;};STACK init_stack(void);void delete_stack(STACK);ElementTP top(STACK);void push(STACK, ElementTP);ElementTP pop(STACK);int is_null(STACK);

這一段程式聲明了一些結構體和指標,以及棧操作的函數原型。當我們其他程式中調用庫時 (比如建立一個棧,或者執行pop操作),同樣需要寫這些聲明。我們把這些在實際調用中需要的聲明儲存到一個標頭檔mystack.h。在實際調用的程式中,可以簡單的include該標頭檔,避免了每次都寫這些聲明語句的麻煩。

 

經過清理後的C程式為mystack.c:


/* By Vamei *//* use single-linked list to implement stack */#include <stdio.h>#include <stdlib.h>
#include "mystack.h"


/* * initiate the stack * malloc the head node. * Head node doesn‘t store valid data * head->next is the top node */STACK init_stack(void){ position np; STACK sk; np = (position) malloc(sizeof(struct node)); np->next = NULL; // sk->next is the top node sk = np; return sk;}/* pop out all elements * and then delete head node */void delete_stack(STACK sk){ while(!is_null(sk)) { pop(sk); } free(sk);}/* * View the top frame */ElementTP top(STACK sk){ return (sk->next->element);}/* * push a value into the stack */void push(STACK sk, ElementTP value) { position np, oldTop; oldTop = sk->next; np = (position) malloc(sizeof(struct node)); np->element = value; np->next = sk->next; sk->next = np; }/* * pop out the top value */ElementTP pop(STACK sk){ ElementTP element; position top, newTop; if (is_null(sk)) { printf("pop() on an empty stack"); exit(1); } else { top = sk->next; element = top->element; newTop = top->next; sk->next = newTop; free(top); return element; } }/* check whether a stack is empty*/int is_null(STACK sk){ return (sk->next == NULL);}

#include "..."; 語句將首先在工作目錄尋找相應檔案。如果使用gcc時,增加-I選項,將在-I提供的路徑中尋找。

 

製作.so檔案

我們的目標是製作共用庫,即.so檔案。

 

首先,編譯stack.c:

$gcc -c -fPIC -o mystack.o mystack.c

-c表示只編譯(compile),而不串連。-o選項用於說明輸出(output)檔案名稱。gcc將產生一個目標(object)檔案mystack.o。

注意-fPIC選項。PIC指Position Independent Code。共用庫要求有此選項,以便實現動態串連(dynamic linking)。

 

產生共用庫:

$gcc -shared -o libmystack.so mystack.o

庫檔案以lib開始。共用庫檔案以.so為尾碼。-shared表示產生一個共用庫。

 

這樣,共用庫就完成了。.so檔案和.h檔案都位於當前工作路徑(.)。

 

使用共用庫

我們編寫一個test.c,來實際調用共用庫:

#include <stdio.h>#include "mystack.h"
/*
* call functions in mystack library
*/void main(void){ ElementTP a; int i; STACK sk; sk = init_stack(); push(sk, 1); push(sk, 2); push(sk, 8); printf("Stack is null? %d\n", is_null(sk)); for (i=0; i<3; i++) { a = pop(sk); printf("pop: %d\n", a); } printf("Stack is null? %d\n", is_null(sk)); delete_stack(sk);}

注意,我們在程式的一開始include了mystack.h。

 

編譯上述程式。編譯器需要知道.h檔案位置。

  • 對於#include "...",編譯器會在當前路徑搜尋.h檔案。你也可以使用-I選項提供額外的搜尋路徑,比如-I/home/vamei/test。
  • 對於#include <...>,編譯器會在預設include搜尋路徑中尋找。

編譯器還需要知道我們用了哪個庫檔案,在gcc中:

  • 使用-l選項說明庫檔案的名字。這裡,我們將使用-lmystack (即libmystack庫檔案)。
  • 使用-L選項說明庫檔案所在的路徑。這裡,我們使用-L. (即.路徑)。

如果沒有提供-L選項,gcc將在預設庫檔案搜尋路徑中尋找。

 

你可以使用下面的命令,來獲知自己電腦上的include預設搜尋路徑:

$`gcc -print-prog-name=cc1` -v   

獲知庫預設搜尋路徑:

$gcc -print-search-dirs

 

我們所需的.h和.so檔案都在當前路徑,因此,使用如下命令編譯:

$gcc -o test test.c -lmystack -L.

將產生test可執行檔。

 

使用

$./test

執行程式

 

運行程式

儘管我們成功編譯了test可執行檔,但很有可能不能執行。一個可能是許可權問題。我們需要有執行該檔案的許可權,見Linux檔案管理背景知識

另一個情況是:

./test: error while loading shared libraries: libmystack.so: cannot open shared object file: No such file or directory

 

這是因為作業系統無法找到庫。libmystack.so位於當前路徑,位於庫檔案的預設路徑之外。儘管我們在編譯時間(compile time)提供了.so檔案的位置,但這個資訊並沒有寫入test可執行檔(runtime)。可以使用下面命令測試:

$ldd test

ldd用於顯示可執行檔所依賴的庫。顯示:

    linux-vdso.so.1 =>  (0x00007fff31dff000)    libmystack.so => not found    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fca30de7000)    /lib64/ld-linux-x86-64.so.2 (0x00007fca311cb000)

這說明test可執行檔無法找到它所需的libmystack.so庫檔案。

 

為瞭解決上面的問題,我們可以將.so檔案放入預設搜尋路徑中。但有時,特別是多使用者環境下,我們不享有在預設搜尋路徑寫入的許可權。

一個解決方案是設定LD_LIBRARY_PATH環境變數。比如:

$export LD_LIBRARY_PATH=.

這樣,可執行檔執行時,作業系統將在先在LD_LIBRARY_PATH下搜尋庫檔案,再到預設路徑中搜尋。環境變數的壞處是,它會影響所有的可執行程式。如果我們在編譯其他程式時,如果我們不小心,很可能導致其他可執行檔無法運行。因此,LD_LIBRARY_PATH環境變數多用於測試。

另一個解決方案,即提供-rpath選項,將搜尋路徑資訊寫入test檔案(rpath代表runtime path)。這樣就不需要設定環境變數。這樣做的壞處是,如果庫檔案移動位置,我們需要重新編譯test。使用如下命令編譯test.c:

$gcc -g -o test test.c -lmystack -L. -Wl,-rpath=.

-Wl表示,-rpath選項是傳遞給連接器(linker)。

 

test順利執行的結果為:

Stack is null? 0pop: 8pop: 2pop: 1Stack is null? 1

C編譯: 動態串連庫 (.so檔案)(轉摘)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.