本文經野火《i.MX Linux開發實戰指南》整理而來。
文章目錄
-
- 1.Ubuntu16.04系統下的HelloWorld
-
- (1)建立工作目錄
- (2)編寫代碼檔案
- (3)編譯并執行
- 2.GCC介紹
-
- (1)GCC工具鍊元件
- (2)基本指令文法
- (3)GCC編譯過程
- (4)連結過程特别說明
1.Ubuntu16.04系統下的HelloWorld
(1)建立工作目錄
mkdir -p ~/workdir/example/hello_c
(2)編寫代碼檔案
使用vscode編寫以下代碼并儲存
#include <stdio.h>
int main()
{
printf("hello, world! This is a C program.\n");
for(int i=0;i<10;i++ ){
printf("output i=%d\n",i);
}
return 0;
}
(3)編譯并執行
#在目标檔案目錄下執行指令
gcc hello.c –o hello #使用gcc把hello.c編譯成hello程式
ls #檢視目錄下的檔案
./hello #執行生成的hello程式
#若提示權限不夠或不是可執行檔案,執行如下指令再運作hello程式
chmod u+x hello #給hello檔案添加可執行權限
2.GCC介紹
(1)GCC工具鍊元件
GCC編譯工具鍊是指以GCC編譯器為核心的一套工具,用于把源代碼轉化成可執行程式,主要包含以下三部分:
- gcc-core:即GCC編譯器,用于完成預處理和編譯過程,例如把C代碼轉換成彙編代碼。
- Binutils :除GCC編譯器外的一系列小工具包括了連結器ld,彙編器as、目标檔案格式檢視器readelf等。
- glibc:包含了主要的 C語言标準函數庫,C語言中常常使用的列印函數printf、malloc函數就在glibc 庫中。
(2)基本指令文法
gcc [選項] 輸入的檔案名
常用選項:
- -o:小寫字母”o”,指定生成的可執行檔案的名字,不指定的話生成的可執行檔案名為a.out。
- -E:隻進行預處理,既不編譯,也不彙編。
- -S:隻編譯,不彙編。
- -c:編譯并彙編,但不進行連結。
- -g:生成的可執行檔案帶調試資訊,友善使用gdb進行調試。
- -Ox:大寫字母”O”加數字,設定程式的優化等級,如”-O0”“-O1” “-O2” “-O3”, 數字越大代碼的優化等級越高,編譯出來的程式一般會越小,但有可能會導緻程式不正常運作。
(3)GCC編譯過程
GCC 編譯工具鍊在編譯一個C源檔案時需要經過以下 4 步:
- 預處理:對源代碼檔案中的檔案包含(include)、 預編譯語句(如宏定義define等)進行展開,生成.i檔案。可了解為把頭檔案的代碼、宏之類的内容轉換成更純粹的C代碼,頭檔案中引用的内容彙總到一處,生成的檔案以.i為字尾。
- 編譯,把預處理後的.i檔案通過編譯成為彙編語言,生成.s檔案,即把代碼從C語言轉換成彙編語言(編譯器)
- 彙編,将彙編語言檔案經過彙編,生成目标檔案的.o檔案,每一個源檔案都對應一個目标檔案。即把彙編語言的代碼轉換成機器碼(彙編器)。
- 連結,将每個源檔案對應的.o檔案連結起來,就生成一個可執行程式檔案(連結器)。
#直接編譯成可執行檔案
gcc hello.c -o hello
#以上指令等價于執行以下全部操作
#預處理,可了解為把頭檔案的代碼彙總成C代碼,把*.c轉換得到*.i檔案
gcc –E hello.c –o hello.i
#編譯,可了解為把C代碼轉換為彙編代碼,把*.i轉換得到*.s檔案
gcc –S hello.i –o hello.s
#彙編,可了解為把彙編代碼轉換為機器碼,把*.s轉換得到*.o,即目标檔案
gcc –c hello.s –o hello.o
#連結,把不同檔案之間的調用關系連結起來,把一個或多個*.o轉換成最終的可執行檔案
gcc hello.o –o hello
Linux下生成的*.o目标檔案、*.so動态庫檔案以及連結生成的可執行檔案都是elf格式的, 可以使用”readelf”工具來檢視它們的内容:
#在檔案目錄執行指令
readelf -a hello.o
(4)連結過程特别說明
例如一個工程裡包含了A和B兩個代碼檔案,編譯後生成了A.o和B.o目标檔案, 如果在代碼A中調用了B中的某個函數fun,那麼在A的代碼中隻要包含了fun的函數聲明, 編譯就會通過,而不管B中是否真的定義了fun函數(當然,如果函數聲明都沒有,編譯也會報錯)。 也就是說A.o和B.o目标檔案在編譯階段是獨立的,而在連結階段, 連結過程需要把A和B之間的函數調用關系理順,也就是說要告訴A在哪裡能夠調用到fun函數, 建立映射關系,是以稱之為連結。若連結過程中找不到fun函數的具體定義,則會連結報錯。
連結分為兩種:
- 動态連結:GCC編譯時的預設選項。動态是指在應用程式運作時才去加載外部的代碼庫,例如printf函數的C标準代碼庫*.so檔案存儲在Linux系統的某個位置,hello程式執行時調用庫檔案*.so中的内容,不同的程式可以共用代碼庫。 是以動态連結生成的程式比較小,占用較少的記憶體。
- 靜态連結,連結時使用選項”–static”,它在編譯階段就會把所有用到的庫打包到自己的可執行程式中。靜态連結的優點是具有較好的相容性,不依賴外部環境,但是生成的程式比較大。
#動态連結,生成名為hello的可執行檔案
gcc hello.o –o hello
#靜态連結,使用--static參數,生成名為hello_static的可執行檔案
gcc hello.o –o hello_static --static
在Ubuntu下,可以使用ldd工具檢視動态檔案的庫依賴,而靜态檔案沒有庫依賴:
#在hello所在的目錄執行如下指令
ldd hello
ldd hello_static