文章目錄
- 2.1 首先編譯源檔案產生對象(obj)檔案(main.o, hello_fn.o):
- 2.2 然後從對象檔案編譯動態庫檔案(libhello.so)和靜態庫(libhello.a)
- 3.1 如果不連結庫的情況下編譯一個主程式是:
- 3.2 連結到動態庫libhello.so
- 3.3 連結到靜態庫libhello.a
Linux C 編程入門之一:gcc 編譯動態庫和靜態庫
cheungmine
2012
1 準備工作
Windows7+Cygwin+gcc
在同一個目錄下準備好下面3個檔案,其中3-2,3-3用來產生動態庫或靜態庫:
主調用程式原始碼3-1:main.c
/** * main.c */#include <stdio.h>#include <math.h>#include "hello_fn.h"intmain (){ hello("cheungmine"); printf("sqrt(2.0) = %f\n", sqrt(2.0)); return 0;}
庫原始碼3-2:hello_fn.c
/** * hello_fn.c */#include <stdio.h>void hello(const char *msg){ printf("Hello %s!\n", msg);}
庫標頭檔原始碼3-3:hello_fn.h
/** * hello_fn.h */void hello(const char *msg);
2 編譯庫2.1 首先編譯源檔案產生對象(obj)檔案(main.o, hello_fn.o):
$ gcc -W -Wall -ansi -pedantic -I. -c main.c$ gcc -W -Wall -ansi -pedantic -I. -c hello_fn.c
2.2 然後從對象檔案編譯動態庫檔案(libhello.so)和靜態庫(libhello.a)
$ gcc -shared hello_fn.o -o libhello.so或者直接從原始碼編譯:$ gcc -shared -I. hello_fn.c -o libhello.so編譯靜態庫相對簡單,就是相當於目標檔案歸檔:$ ar r libhello.a hello_fn.o
3 編譯使用庫的主程式3.1 如果不連結庫的情況下編譯一個主程式是:
$ gcc main.o -o main或者$ gcc -c main.c -o main
但是由於我們在main.c的代碼中寫固定了調用庫的代碼(hello函數),所以,必須連結到這個庫才行。
3.2 連結到動態庫libhello.so
$ gcc main.o -o main ./libhello.so
這樣在目前的目錄下就產生了:main.exe(我的cygwin環境,Linux環境下沒有副檔名)
運行這個main.exe:
$ ./main.exe
刪除libhello.so,再運行main.exe會報錯誤:error while loading shared libraries: libhello.so: cannot open shared object...
3.3 連結到靜態庫libhello.a
$ gcc main.o -o main2 ./libhello.a
刪除libhello.a,運行main2.exe,一切正常。說明程式的確連結到靜態庫了。
4 查看程式依賴的庫
$ file main.exe main2.exe$ ldd main.exe main2.exe
如果我們的動態庫libhello.so與主程式不在同一個目錄下,怎麼辦?
複製libhello.so和libhello.a到另外一個目錄,比如:/cygdrive/c/temp,那麼編譯主程式為:
$ gcc main.o -o main /cygdrive/c/temp/libhello.so執行:$ export PATH=/cygdrive/c/temp:$PATH$ ./main.exe
5 運行時載入動態庫
修改main.c檔案為如下清單:
/** * main.c */#include <stdio.h>#include <math.h>#include <dlfcn.h>#include "hello_fn.h"void dl_hello(){ void *dp; char *error; void (*fn_hello)(const char*); dp = dlopen("libhello.so", RTLD_LAZY ); if(!dp) { printf("%s\n", dlerror()); exit(1); } fn_hello = dlsym(dp, "hello"); if(!fn_hello) { printf("%s\n", dlerror()); exit(1); } fn_hello("cheungmine: load library when running"); dlclose(dp);} intmain (){// hello("cheungmine"); dl_hello(); printf("sqrt(2.0) = %f\n", sqrt(2.0)); return 0;}
然後重新編譯main.exe和libhello.so如下:
編譯源檔案$ gcc -Wall -I. -c main.c$ gcc -Wall -I. -c hello_fn.c編譯動態庫$ gcc -shared hello_fn.o -o libhello.so連結主程式,但不連結到動態庫。$ gcc main.o -o main.exe執行下面的代碼可以看到libhello.so並不在main.exe的依存之列:$ ldd main.exe移動庫到其他目錄,通過修改環境變數,程式main.exe執行正確: $ mv libhello.so /cygdrive/c/temp$ export PATH=.:/cygdrive/c/temp:$PATH$ ./main.exe
6 總結
通過上面的練習,基本清楚了如何用gcc編譯器,包括靜態連結庫和動態連結程式庫。通過下面的表格可以看到
Linux和Windows的區別:
Windows Unix/Linux
----------------------------------------------------------------------
靜態連結庫 hello.lib libhello.a
動態連結程式庫 hello.dll libhello.so
消極式載入 LoadLibrary dlopen
GetProcAddress dlsym
FreeLibrary dlclose
本文全部內容在cygwin上啟動並執行,和真實的Linux環境還是存在差異的。gcc版本3.4.4。