linux成長之路(gcc編譯器、靜態庫、動態庫)

來源:互聯網
上載者:User

標籤:linux   linjm   應用程式   機器視覺   

Jeremy Lin

GCC簡介

GCC(GNU Complier Collection)是GNU推出的功能強大、效能優越的多平台編譯器套件,它包括了C、C++、Objective-C、Fortran、Java、Ada和Go語言的前端,也包括了這些語言的庫,當前最新的版本是GCC 5.1。GCC可以在多種硬體平台上編譯出可執行程式,其執行效率與一般的編譯器相比平均效率要高20%-30%。GCC編譯器能將C、C++語言來源程式、匯程式程式和目標程式編譯、串連成可執行檔,如果沒有給出可執行檔的名字,GCC將產生一個名為 a.out 的檔案。在Linux系統中,可執行檔沒有統一的尾碼,系統從檔案的屬性來區分可執行檔和不可執行檔。而GCC通過尾碼來區分輸入檔案的類別,下面我們來介紹GCC所遵循的部分約定規則:

.c的檔案                         C語言原始碼檔案;

.a的檔案                        由目標檔案構成的檔案庫檔案;

.C,.cc或.cxx 的檔案     C++原始碼檔案且必須要經過預先處理;

.h的檔案                         程式所包含的標頭檔;

.i 的檔案                         C原始碼檔案且不應該對其執行預先處理;

.ii的檔案                         C++原始碼檔案且不應該對其執行預先處理;

.m的檔案                        Objective-C原始碼檔案;

.mm的檔案                     Objective-C++原始碼檔案;

.o的檔案                         編譯後的目標檔案;

.s的檔案                         組合語言原始碼檔案;

.S的檔案                         經過先行編譯的組合語言原始碼檔案。


GCC執行過程

雖然我們稱gcc是C語言的編譯器,但適用gcc由C語言原始碼產生可執行檔的過程不僅僅是編譯的過程,而是要經曆4個相互關聯的步驟:

(1)預先處理(Preprocessing)

(2)編譯(Compilation)

(3)彙編(Assembly)

(4)串連(Linking)


(1)預先處理階段:輸入的是C語言的源檔案,通常為*.c。它們通常帶有.h之類的標頭檔。這個階段主要是處理源檔案中的#ifdef、#include和#define命令。該階段會產生一個中間檔案*.i,但實際工作中通常不會專門產生這種檔案,因為基本上用不到;若非要產生這種檔案,可以利用下面的命令:

gcc -E hello.c -o hello.i


hello.c的原始碼如下:

#include <stdio.h>int main(int argc, char**argv){printf("Hello Linux\n");return 0;}


產生的hello.i的結果如下(很長,所以只截取一部分看看)


……

……



(2)編譯階段:輸入的是中間檔案*.i,編譯後產生組合語言檔案*.s。這個階段對應的GCC命令如下:

gcc -S hello.i -o hello.s

如下所示:



(3)彙編階段:輸入的是彙編檔案*.s,輸出的轉換產生的機器語言*.o。這個階段對應的命令如下:

gcc -c hello.s -o hello.o


(4)串連階段:將輸入的機器代碼檔案*.s,彙集成一個可執行檔二進位檔案。命令如下:

gcc hello.o -o helloX




GCC基本用法與選項

在使用GCC編譯器的時候,我們必須給出一系列必要的調用參數和檔案名稱。GCC編譯器的調用參數大約有100多個,其中多數參數我們可能根本就用不到,這裡只介紹其中最集中、最常用的參數。

GCC的基本用法:


gcc [options] [filenames]


其中options就是編譯器所需要的參數,filenames給出相關的檔案名稱。

-c,  只編譯,不串連成為可執行檔。編譯器只是由輸入的*.c等原始碼檔案產生*.o目標檔案,通常用於編譯不包含主程式的子程式檔案;

-o output_filename,確定輸出檔案的名稱為output_filename,同時這個名稱不能和源檔案同名。如果不給出這個選項,gcc給出預設的可執行檔a.out;

-g,產生符號調試工具(GNU的gdb)所必要的符號資訊,要想對原始碼進行調試,我們必須加入這個選項;

-O,對程式進行最佳化編譯、串連,採用這個選項,整個原始碼會在編譯、串連過程中進行最佳化處理,這樣產生的可執行檔的執行效率可以提高,但是,編譯、串連的速度相應的要慢一些;

-O2,比-O更好的最佳化編譯、串連,相應的速度會更慢一些;

-Wall,編譯警告選項,在編譯過程中如果gcc遇到一些它認為可能發生錯誤的地方就會提出一些相應的警告和提示訊息;

-I dirname,將dirname所指出的目錄加入到程式標頭檔搜尋目錄列表中,是在先行編譯過程中使用的參數;

-S,編譯指定的源檔案,但不進行彙編;

-E,預先處理指定的源檔案,不進行編譯;


編譯靜態庫

靜態庫是一些目標代碼的集合。Linux環境下的靜態庫目標檔案一般是以 .a 作為目標檔案的副檔名。

Linux 環境下使用 ar 命令建立一個靜態庫。靜態庫的優點在於使用簡單,編譯快速。靜態庫在應用程式產生時,已經編譯成為可重定位的目標檔案,因此可以不必要再編譯,節省編譯時間,以最短的時間產生可執行程式。而且,靜態庫在編譯的時候已經複製到可執行程式的代碼中,不需要像使用動態庫那樣再程式載入時再次連結。在理論上,可執行程式使用靜態庫,要比使用動態庫速度快1%~5%。

(1)建立靜態庫

假設有一原始碼 static_lib.c:

int add( int a, int b ){return a + b;}int sub( int a, int b ){return a - b;}int mul( int a, int b ){return a * b;}int div( int a, int b ){return a / b;}
現在要將其編譯成為靜態庫檔案——static_lib.a,則可使用如下的命令:

gcc -c static_lib.car rcs static_lib.a static_lib.c
其中 ar 可以將目標檔案加入到一個已經存在的靜態庫中(若不存在則建立),rcs 參數分別表示:把列表中的目標檔案加入到靜態庫中(參數r),若指定靜態庫不存在,則建立該靜態庫檔案(參數c),最後更新靜態庫檔案的索引,使之包含新加入的目標檔案的內容(參數s)。




(2)使用靜態庫

靜態庫建立成功後,需要連結到應用程式中使用。應用程式可以引用該庫中的函數或者全域變數。為了使應用程式可以正確引用該庫中的全域符號,需要製作一個包含該靜態庫中全域標記法宣告的檔案。這個標頭檔可以包含在應用程式的標頭檔中,這樣就可以引用靜態庫中的全域符號了。

標頭檔static_lib.h

extern int add( int a, int b);extern int sub( int a, int b );extern int mul( int a, int b );extern int div( int a, int b );
應用程式main.c:

#include <stdio.h>#include "static_lib.h"int main(void){int a, b;printf("please input a and b \n");scanf( "%d%d", &a, &b );printf("the add:  %d", add(a,b));printf("the sub:  %d", sub(a,b));printf("the mul:  %d", mul(a,b));printf("the div:  %d", div(a,b));return 0;}
我們使用 gcc 的 -l 選項來指定靜態庫,或者使用 -L 參數來指定庫檔案的搜尋路徑。-l 和 -L 之後都直接帶參數而不跟空格。因而,對於本例,編譯命令如下:

gcc main.c -lstatic_lib.a -o app
或者使用 “ . ” 作為靜態庫的搜尋路徑,表示在目前的目錄下搜尋需要的靜態庫檔案。

gcc -L. main.c -o app
注意:-l 是連結器選項,一定要放在被編譯的源檔案的檔案名稱之後。

gcc 也支援使用-static 選項對靜態庫進行連結。

gcc main.c -static ./static_lib.a -o app



編譯動態庫

動態庫又稱為共用庫或者動態連結程式庫,現代程式中大量地使用動態連結程式庫。例如,Windows 環境中的dll檔案和Linux環境下的 so檔案。動態庫是在程式啟動時被裝載的。當一個程式裝載了一個動態庫後,其他應用程式仍然可以裝載同一個動態庫。

(1)建立動態庫

假設有一份需要轉化成動態庫的代碼share_lib.c

void insert_sort( int *array, int length){int i, j;for (i = 1; i < length; ++i){int temp;j = i;temp = array[j];while(j > 0){if (temp < array[j - 1]){array[j] = array[j - 1];j--;}elsebreak;}array[j] = temp;}}int binary_search( int *array, int item, int length){int high, low, mid;high = length - 1;low = 0;mid = (high + low) / 2;while(low <= high){if (array[mid] > item){high = mid;mid = (high + low) / 2;}else if ( array[mid] < item){low = mid;mid = (high + low)/2;}elsereturn mid;}return -1;}

Linux使用gcc建立一個動態庫。由於動態庫可以被多個進程共用載入,所以需要產生位置無關的目標檔案。這時需要使用gcc編譯器的-fPIC選項,該選項用於產生位置無關的代碼。除了需要使用-fPIC選項外,還需要使用-shared 選項,該選項將位置無關的代碼製作為動態庫。

gcc -shared -fPIC share_lib.so share_lib.c


(2)使用動態庫

動態庫建立完後,應用程式可以引用該庫中的函數或全域變數。為了使應用程式可以正確引用該庫中的全域符號,需要製作一個包含該動態庫中全域標記法宣告的標頭檔。

如share_lib.h

extern void insert_sort( int *array, int length);extern int binary_search( int *array, int item, int length);
使用動態庫的main.c

#include <stdio.h>#include "share_lib.h" int main(int argc, char const *argv[]){int array[5] = {5,4,2,3,1};int item;int pos;insert_sort( array, 5);printf("please input a number\n");scanf("%d", &item);pos = binary_search( array, item, 5);if (pos == -1){        printf("can't find\n");}else{                      printf("the position is %d\n", pos+1);}return 0;}

使用如下命令建立程式:

gcc main.c ./share_lib.so -o app





本文地址:http://blog.csdn.net/linj_m/article/details/45039347

更多資源請關注 部落格:LinJM-機器視覺  微博:林建民-機器視覺



linux成長之路(gcc編譯器、靜態庫、動態庫)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.