多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > 綜合技術 > gcc編譯解析

gcc編譯解析

來源:程序員人生   發布時間:2016-07-26 13:06:41 閱讀次數:2618次

簡介

本文通過如何調用庫文件來說述gcc程序編譯

正文

甚么是庫
在windows平臺和linux平臺下都大量存在著庫。本質上來講庫是1種可履行代碼的2進制情勢,可以被操作系統載入內存履行。由于windows和linux的平臺不同(主要是編譯器、匯編器和連接器的不同),因此2者庫的2進制是不兼容的。
本文僅限于介紹linux下的庫。
庫的種類
linux下的庫有兩種:靜態庫和同享庫(動態庫)。2者的不同點在于代碼被載入的時刻不同。靜態庫的代碼在編譯進程中已被載入可履行程序,因此體積較大。同享庫的代碼是在可履行程序運行時才載入內存的,在編譯進程中僅簡單的援用,因此代碼體積較小。
庫存在的意義
庫是他人寫好的現有的,成熟的,可以復用的代碼,你可使用但要記得遵照許可協議。現實中每一個程序都要依賴很多基礎的底層庫,不可能每一個人的代碼都從零開始,因此庫的存在乎義非同尋常。
庫文件是如何產生的在linux下
靜態庫的后綴是.a,它的產生分兩步
1. 由源文件編譯生成1堆.o,每一個.o里都包括這個編譯單元的符表
2. ar命令將很多.o轉換成.a,成為靜態庫動態庫的后綴是.so,它由gcc加特定參數編譯產生。
具體方法參見后文實例。
庫文件是如何命名的,有無甚么規范
在linux下,庫文件1般放在/usr/lib和/lib下,靜態庫的名字1般為libxxxx.a,其中xxxx是該lib的名稱。動態庫的名字1般為libxxxx.so.major.minor,xxxx是該lib的名稱,major是主版本號,minor是副版本號
如何知道1個可履行程序依賴哪些庫
ldd命令可以查看1個可履行程序依賴的同享庫,
比如查看ln程序的依賴庫$ ldd /bin/ls

$ ldd /bin/ls
linux-vdso.so.1 => (0x00007ffd22c47000)
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007ff5f2a6f000)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007ff5f2867000)
libacl.so.1 => /lib/x86_64-linux-gnu/libacl.so.1 (0x00007ff5f265f000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff5f22a1000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007ff5f209d000)
/lib64/ld-linux-x86⑹4.so.2 (0x00007ff5f2c8e000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007ff5f1e80000)
libattr.so.1=>/lib/x86_64-linux-gnu/libattr.so.1(0x00007ff5f1c7b000)

可以看到ls命令依賴于librt.so.1庫,libc.so.6庫。。。

用gcc生成靜態和動態連接庫的示例

我們通常把1些公用函數制作成函數庫,供其它程序使用。函數庫分為靜態庫和動態庫兩種。靜態庫在程序編譯時會被連接到目標代碼中,程序運行時將不再需要該靜態庫。動態庫在程序編譯時其實不會被連接到目標代碼中,而是在程序運行是才被載入,因此在程序運行時還需要動態庫存在。
本文主要通過舉例來講明在Linux中如何創建靜態庫和動態庫,和使用它們。為了便于論述,我們先做1部份準備工作。
準備好測試代碼hello.h、hello.c和main.c;
hello.h(見程序1)為該函數庫的頭文件。
hello.c(見程序2)是函數庫的源程序,其中包括公用函數hello,該函數將在屏幕上輸出”HelloXXX!”。
main.c(見程序3)為測試庫文件的主程序,在主程序中調用了公用函數hello。
程序1:hello.h

#ifndefHELLO_H #defineHELLO_H void hello(constchar*name); #endif

程序2:hello.c

#include<stdio.h> void hello(constchar*name){ printf(“Hello%s!\n”,name); }

程序3:main.c

#include“hello.h” int main() { hello(“everyone”); return0; }

問題的提出
注意:這個時候,我們編譯好的hello.o是沒法通過gcc -o編譯的,這個道理非常簡單,hello.c是1個沒有main函數的.c程序,因此不夠成1個完全的程序,如果使用gcc -o編譯并連接它,GCC將報錯。編譯main.c也會出錯,由于找不到hello的函數原型及實現。不管靜態庫,還是動態庫,都是由.o文件創建的。因此,我們必須將源程序hello.c通過gcc先編譯成.o文件。
這個時候我們有3種思路:
1. 通過編譯多個源文件,直接將目標代碼合成1個.o文件。
2. 通過創建靜態連接庫libmyhello.a,使得main函數調用hello函數時可調用靜態連接庫.
3. 通過創建動態連接庫libmyhello.so,使得main函數調用hello函數時可調用靜態連接庫。
思路1:編譯多個源文件
在系統提示符下鍵入以下命令得到hello.o文件。
$ gcc-chello.c
為何不使用gcc -o hello hello.cpp這個道理我們之前已說了,使用-c是甚么意思呢?
這觸及到gcc編譯選項的常識。我們通常使用的gcc –o是將.c源文件編譯成為1個可履行的2進制代碼(-o選項實際上是制定輸出文件文件名,如果不加-c選項,gcc默許會編譯連接生成可履行文件,文件的名稱有-o選項指定),這包括調用作為GCC內的1部份真實的C編譯器(ccl),和調用GNU C編譯器的輸出中實際可履行代碼的外部GNU匯編器(as)和連接器工具(ld)。而gcc -c是使用GNU匯編器將源文件轉化為目標代碼以后就結束,在這類情況下,只調用了C編譯器(ccl)和匯編器(as),而連接器(ld)并沒有被履行,所以輸出的目標文件不會包括作為Linux程序在被裝載和履行時所必須的包括信息,但它可以在以后被連接到1個程序。
我們運行ls命令看看是不是生成了hello.o文件。

$ ls hello.c hello.h hello.o main.c

在ls命令結果中,我們看到了hello.o文件,本步操作完成。
同理編譯main
$ gcc–cmain.c
將兩個文件連接成1個.o文件。
$gcc –o hello hello.o main.o
運行
$./hello
Hello everyone!
完成^^
思路2:靜態連接庫
下面我們先來看看如何創建靜態庫,和使用它。靜態庫文件名的命名規范是以lib為前綴,緊接著跟靜態庫名,擴大名為.a。例如:我們將創建的靜態庫名為myhello,則靜態庫文件名就是libmyhello.a。在創建和使用靜態庫時,需要注意這點。創建靜態庫用ar命令。在系統提示符下鍵入以下命令將創建靜態庫文件libmyhello.a。
$ ar rcs libmyhello.a hello.o
我們一樣運行ls命令查看結果:

$ls hello.c hello.h hello.o libmyhello.a main.c

ls命令結果中有libmyhello.a。
靜態庫制作完了,如何使用它內部的函數呢?只需要在使用到這些公用函數的源程序中包括這些公用函數的原型聲明,然后在用gcc命令生成目標文件時指明靜態庫名,gcc將會從靜態庫中將公用函數連接到目標文件中。注意,gcc會在靜態庫名前加上前綴lib,然后追加擴大名.a得到的靜態庫文件名來查找靜態庫文件,因此,我們在寫需要連接的庫時,只寫名字就能夠,如libmyhello.a的庫,只寫-lmyhello。其實也能夠直接在調用靜態庫的全名即libmyhello.a
在程序3:main.c中,我們包括了靜態庫的頭文件hello.h,然后在主程序main中直接調用公用函數hello。下面生成目標程序hello,然后運行hello程序看看結果如何。

方式1:$ gcc -o hellomain.c –static –L ./ -lmyhello 方式2:$ gcc -o hellomain.c –static –L ./ -libmyhello.a

兩種方式都可以生成已連接靜態庫的hello程序

$./hello Hello everyone!

我們刪除靜態庫文件試試公用函數hello是不是真的連接到目標文件hello中了。

$rm libmyhello.a rm:remove regular file`libmyhello.a’? y $./hello Hello everyone!

程序照舊運行,靜態庫中的公用函數已連接到目標文件中了。
靜態連接庫的1個缺點是,如果我們同時運行了許多程序,并且它們使用了同1個庫函數,這樣,在內存中會大量拷貝同1庫函數。這樣,就會浪費很多珍貴的內存和存儲空間。使用了同享連接庫的Linux就能夠避免這個問題。
同享函數庫和靜態函數在同1個地方,只是后綴有所不同。比如,在1個典型的Linux系統,標準的同享數序函數庫是/usr/lib/libm.so。
當1個程序使用同享函數庫時,在連接階段其實不把函數代碼連接進來,而只是連接函數的1個援用。當終究的函數導入內存開始真正履行時,函數援用被解析,同享函數庫的代碼才真正導入到內存中。這樣,同享連接庫的函數就能夠被許多程序同時同享,并且只需存儲1次就能夠了。同享函數庫的另外一個優點是,它可以獨立更新,與調用它的函數絕不影響。
思路3、動態連接庫(同享函數庫)
我們繼續看看如何在Linux中創建動態庫。我們還是從.o文件開始。動態庫文件名命名規范和靜態庫文件名命名規范類似,也是在動態庫名增加前綴lib,但其文件擴大名為.so。例如:我們將創建的動態庫名為myhello,則動態庫文件名就是libmyhello.so。用gcc來創建動態庫。
在系統提示符下鍵入以下命令得到動態庫文件libmyhello.so。

$ gcc -c -fPIC -o hello.o hello.c $ gcc –shared –o libmyhello.so hello.o

“PIC”命令行標記告知GCC產生的代碼不要包括對函數和變量具體內存位置的援用,這是由于現在還沒法知道使用該消息代碼的利用程序會將它連接到哪1段內存地址空間。這樣編譯出的hello.o可以被用于建立同享連接庫。建立同享連接庫只需要用GCC的”-shared”標記便可。
我們照樣使用ls命令看看動態庫文件是不是生成。

$ls hello.cpphello.hhello.olibmyhello.somain.cpp

在程序中使用動態庫和使用靜態庫完全1樣,也是在使用到這些公用函數的源程序中包括這些公用函數的原型聲明,然后在用gcc命令生成目標文件時指明動態庫名進行編譯。我們先運行gcc命令生成目標文件,再運行它看看結果。
如果直接用以下方法進行編譯,并連接:
$ gcc –o hello main.c –L ./ -lmyhello
(使用”-lmyhello”標記來告知GCC驅動程序在連接階段援用同享函數庫libmyhello.so。“-L ./”標記告知GCC函數庫可能位于當前目錄。否則GNU連接器會查找標準系統函數目錄:它前后搜索
1. elf文件的DT_RPATH段
2. 環境變量LD_LIBRARY_PATH
3. /etc/ld.so.cache文件列表
4. /lib/
5. /usr/lib目錄
找到庫文件后將其載入內存,但是我們生成的同享庫在當前文件夾下,并沒有加到上述的4個路徑的任何1個中,因此,履行后會出現毛病)

$./hello ./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory

毛病提示,找不到動態庫文件libmyhello.so。程序在運行時,會在/usr/lib和/lib等目錄中查找需要的動態庫文件。若找到,則載入動態庫,否則將提示類似上述毛病而終止程序運行。有多種方法可以解決,
我們將文件libmyhello.so復制到目錄/usr/lib中,再試試。

$mv libmyhello.so /usr/lib $./hello

成功!
既然連接器會搜索LD_LIBRARY_PATH所指定的目錄,那末我們可以將這個環境變量設置成當前目錄:
先履行:
$exportLD_LIBRARY_PATH=$(pwd)
再履行:
$./hello
成功!
履行:
sudo ldconfig /usr/local/lib
注:當用戶在某個目錄下面創建或拷貝了1個動態連接庫,若想使其被系統同享,可以履行1下”ldconfig目錄名“這個命令.此命令的功能在于讓ldconfig將指定目錄下的動態連接庫被系統同享起來,意即:在緩存文件/etc/ld.so.cache中追加進指定目錄下的同享庫.本例讓系統同享了/usr/local/lib目錄下的動態連接庫.該命令會重建/etc/ld.so.cache文件
成功!
這也進1步說明了動態庫在程序運行時是需要的。
可以查看程序履行時調用動態庫的進程:
$ld dhello
履行test,可以看到它是如何調用動態庫中的函數的。

$lddhello linux-gate.so.1=>(0x00110000) libmyhello.so=>/usr/lib/libmyhello.so(0x00111000) libc.so.6=>/lib/libc.so.6(0x00859000) /lib/ld-linux.so.2(0x0083a000)

ok,程序調用動態庫的功能完成。

gcc編譯解析

C和C++編譯器是集成的,它們都需要1個或多個處理輸入文件:預處理(perprocessing),編譯(compilation),匯編(assembly)和連接(linking)。
整體選項
-c :編譯或匯編文件,但是不做連接。缺省情況下,GCC通過使用‘.o’來替換源文件名后綴‘.c’,‘.i’,‘.s’等等
-o file: 指定輸出的可履行文件為file且只能輸出1個可履行文件,如果沒有使用‘-o’選項及命令為gcc xx.c。
連接器選項
-shared:生成1個同享目標文件,它可以和其它目標文件連接產生可履行文件。
-llibrary: 連接名為library的庫文件。連接器會在標準搜索目錄中尋覓這個庫文件。搜索目錄除1些標準的搜索目錄外還包括用戶使用‘-L’選項指定的路徑
目錄選項
-Ldir:在‘-L’選項的搜索連接列表主功能添加dir目錄
LD_LIBRARY_PATH:這個環境變量唆使動態連接器可以裝載動態庫的路徑。
代碼生成選項:
-fPIC:表示編譯為位置獨立的代碼(適用于同享庫),不用此選項的話編譯后的代碼是位置相干的所以動態載入時是通過代碼拷貝的方式來滿足不同進程的需要,而不能到達真正代碼段同享的目的。
正告選項
-Wall:顯示大部份常見的正告,如:定義了變量沒有使用,變量未初始化等等
調試選項
-g:以操作系統的本地格式產生調試信息,GDB能夠使用這些調試信息,進行程序調試

參考文章:Linux下Gcc生成和使用靜態庫和動態庫詳解(轉)

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 免费观看亚洲 | 日本理论午夜中文字幕第一页 | 夜夜爽www | 国产成人毛片精品不卡在线 | 黄色在线网站视频 | 欧美亚洲国产成人精品 | 最近免费中文字幕视频高清在线看 | 人善交video 人善交videos欧美3 | 伊人网亚洲 | 国产一区日韩二区欧美三 | 欧美一区二区视频三区 | 久久久久日韩精品免费观看网 | 亚洲视频在线免费观看 | 亚洲第一色区 | 亚洲另类春色校园小说 | 激情久久久久久久久久 | 亚洲春色在线视频 | 日本亚洲免费 | 亚洲经典一区二区三区 | 久久综合九九亚洲一区 | 日本香蕉视频 | 日本福利片午夜免费观着 | 欧美性最猛xxxx在线观看视频 | 欧美性性性 | 午夜影院啊啊啊 | 性欧美高清精品videos | 日韩精品一区二区三区中文精品 | 欧美成人在线观看 | 日韩欧美精品 | 一本大道卡一卡二卡三视频 | 精品国产毛片 | 亚色最新网址 | 日韩在线不卡一区在线观看 | 韩日一级视频 | 欧美一区二区三区久久综 | 色人阁亚洲 | 伊人国产在线 | 久久亚洲伊人 | 久久ri精品高清一区二区三区 | 狠狠的撞进去嗯啊h女强男视频 | 天天综合久久久网 |