Linux編譯工具:gcc入門

1. 什麼是gcc

gcc的全稱是GNU Compiler Collection,它是一個可以編譯多種語言的編譯器。最開始gcc是做爲C語言的編譯器(GNU C Compiler),如今除了c語言,還支持C++、java、Pascal等語言。gcc支持多種硬件平臺。html

2. gcc的特色

  • gcc是一個可移植的編譯器,支持多種硬件平臺。例如ARM、X86等等。
  • gcc不只是個本地編譯器,它還能跨平臺交叉編譯。所謂的本地編譯器,是指編譯出來的程序只可以在本地環境進行運行。而gcc編譯出來的程序可以在其餘平臺進行運行。例如嵌入式程序可在x86上編譯,而後在arm上運行。
  • gcc有多種語言前端,用於解析不一樣的語言。
  • gcc是按模塊化設計的,能夠加入新語言和新CPU架構的支持。
  • gcc是自由軟件。任何人均可以使用或更改這個軟件。

3. gcc編譯程序的過程

gcc編譯程序主要通過四個過程:前端

  • 預處理(Pre-Processing)
  • 編譯 (Compiling)
  • 彙編 (Assembling)
  • 連接 (Linking)

預處理其實是將頭文件、宏進行展開。編譯階段,gcc調用不一樣語言的編譯器,例如c語言調用編譯器ccl。gcc其實是個工具鏈,在編譯程序的過程當中調用不一樣的工具。彙編階段,gcc調用匯編器進行彙編。連接過程會將程序所須要的目標文件進行連接成可執行文件。彙編器生成的是可重定位的目標文件,學過操做系統,咱們知道,在源程序中地址是從0開始的,這是一個相對地址,而程序真正在內存中運行時的地址確定不是從0開始的,並且在編寫源代碼的時候也不能知道程序的絕對地址,因此重定位可以將源代碼的代碼、變量等定位爲內存具體地址。下面以一張圖來表示這個過程,注意過程當中文件的後綴變化,編譯選項和這些後綴有關。java

這是GCC編譯的四個步驟。編程

4. gcc經常使用選項

來看一下gcc經常使用選項bash

選項名 做用
-o 產生目標(.i、.s、.o、可執行文件等)
-E 只運行C預編譯器
-S 告訴編譯器產生彙編程序文件後中止編譯,產生的彙編語言文件拓展名爲.s
-c 通知gcc取消鏈接步驟,即編譯源碼,並在最後生成目標文件
-Wall 使gcc對源文件的代碼有問題的地方發出警告
-Idir 將dir目錄加入搜索頭文件的目錄路徑
-Ldir 將dir目錄加入搜索庫的目錄路徑
-llib 鏈接lib庫
-g 在目標文件中嵌入調試信息,以便gdb之類的調試程序調試

如今咱們有源文件hello.c,下面是一些gcc的使用示例:架構

gcc -E hello.c -o hello.i   對hello.c文件進行預處理,生成了hello.i 文件
gcc -S hello.i -o hello.s    對預處理文件進行編譯,生成了彙編文件
gcc -c hello.s -o hello.o  對彙編文件進行編譯,生成了目標文件
gcc hello.o -o hello 對目標文件進行連接,生成可執行文件
gcc hello.c -o hello 直接編譯連接成可執行目標文件
gcc -c hello.c 或 gcc -c hello.c -o hello.o 編譯生成可重定位目標文件

使用gcc時能夠加上-Wall選項。下面這個例子若是不加上-Wall選項,編譯器不會報出任何錯誤或警告,可是程序的結果卻不是預期的:模塊化

//bad.c
#include<stdio.h>
int main()
{
    printf("the number is %f ",5);  //程序輸出了the number is 0.000000,結果錯誤
    return 0;
 }

使用-Wall選項:函數

gcc -Wall bad.c -o bad工具

gcc將輸出警告信息:操作系統

warning: format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘int’ [-Wformat=]
printf("the number is %f\n",5);

5. gcc編譯多個文件

假設如今有三個文件:hello.c hello.h main.c ,三個文件的內容以下:

// hello.c
#include<stdio.h>
#include"hello.h"
void printHello()
{
        printf("hello world!\n");
}
//main.c
#include<stdio.h>
#include"hello.h"
int main()
{
        printHello();
        return 0;
}
//hello.h
//僅包含函數聲明
#ifndef _HELLO_
#define _HELLO_
void printHello();
#endif

編譯這三個文件,能夠一次編譯:

gcc hello.c main.c -o main 生成可執行文件main

也能夠獨立編譯:

gcc -Wall -c main.c -o main.o
gcc -Wall -c hello.c -o hello.o
gcc -Wall main.o hello.o -o main

獨立編譯的好處是,當其中某個模塊發送改變時,只須要編譯該模塊就行,沒必要從新編譯全部文件,這樣能夠節省編譯時間。

6. 使用外部庫

在使用C語言和其餘語言進行程序設計的時候,咱們須要頭文件來提供對常數的定義和對系統及庫函數調用的聲明。庫文件是一些預先編譯好的函數集合,那些函數都是按照可重用原則編寫的。它們一般由一組互相關聯的可重用原則編寫的,它們一般由一組互相關聯的用來完成某項常見工做的函數構成。使用庫的優勢在於:

  • 模塊化的開發
  • 可重用性
  • 可維護性

庫又能夠分爲靜態庫與動態庫:

  • 靜態庫(.a):程序在編譯連接的時候把庫的代碼連接到可執行文件中。程序運行的時候將再也不須要靜態庫。靜態庫比較佔用磁盤空間,並且程序不能夠共享靜態庫。運行時也是比較佔內存的,由於每一個程序都包含了一份靜態庫。

  • 動態庫(.so或.sa):程序在運行的時候纔去連接共享庫的代碼,多個程序共享使用庫的代碼,這樣就減小了程序的體積。

通常頭文件或庫文件的位置在:

  • /usr/include及其子目錄底下的include文件夾
  • /usr/local/include及其子目錄底下的include文件夾
  • /usr/lib
  • /usr/local/lib
  • /lib

7. 生成靜態庫

爲了生成.a文件,咱們須要先生成.o文件。下面這行命令將咱們的hello.o打包成靜態庫libhello.a:

ar rcs libhello.a hello.o

ar是gun歸檔工具,rcs表示replace and create,若是libhello以前存在,將建立新的libhello.a並將其替換。

而後就能夠這樣來使用靜態庫libhello.a

gcc -Wall main.c libhello.a -o main

還有另一種使用方式:

gcc -Wall -L. main.c -o main -lhello 【lhello 是 libhello的縮寫】

其中 -L.表示庫文件的位置在當前目錄下,因爲libhello.a是咱們本身生成的,並存放在當前錄下下,因此須要加上-L.選項。默認庫文件是在系統的目錄下進行搜索。一樣的,-I.選項用於頭文件的搜索。

8. 生成共享庫

生成一個共享庫,名稱的規則是libxxx.so。將剛纔hello.o生成libhello.so的命令爲:

gcc -shared -fPIC hello.o -o libhello.so

生成了共享庫以後,能夠這樣來使用共享庫:

gcc -Wall main.o -o main -L. -lhello

該命令與使用靜態庫的命令相同,可是在共享庫與靜態庫共存的狀況下,優先使用共享庫。

共享庫有時候並不不在當前的目錄下,爲了讓gcc可以找獲得共享庫,有下面幾種方法:

  1. 拷貝.so文件到系統共享庫路徑下,通常指/usr/lib
  2. 在~/.bash_profile文件中,配置LD_LIBRARY_PATH變量
  3. 配置/etc/ld.so.conf,配置完成後調用ldconfig更新ld.so.cache

其中,shared選項表示生成共享庫格式。fPIC表示產生位置無關碼(position independent code),位置無關碼錶示它的運行、加載與內存位置無關,能夠在任何內存地址進行加載。

9. 庫的搜索路徑

庫的搜索路徑遵循幾個搜索原則:從左到右搜索-I -l指定的目錄,若是在這些目錄中找不到,那麼gcc會從由環境 變量指定的目錄進行查找。頭文件的環境變量是C_INCLUDE_PATH,庫的環境變量是LIBRARY_PATH.若是仍是找不到,那麼會從系統指定指定的目錄進行搜索。

文章連接:http://www.cnblogs.com/QG-whz/p/5456720.html

相關文章
相關標籤/搜索