1. 程式人生 > >和菜鳥一起學c之gcc編譯過程及其常用編譯選項

和菜鳥一起學c之gcc編譯過程及其常用編譯選項

       上篇文章,知道了,C程式碼編譯後存放在記憶體中的位置,那麼C程式碼的整個編譯過程又是怎樣的呢?一條命令gcc hello.c就可以編譯成可執行程式a.out,然後./a.out之後就可以執行hello.c這個程式的程式碼了。下面的文章分析的不錯,就整理了下。

hello.c:

#include<stdio.h>
int main()
{
        printf(“Hello World\n”);
        return 0;
}

實際上gcc hello.c可以分解為4個步驟,分別是預處理(Preprocess),編譯(Compilation),彙編(Assembly)和連結(Linking)。

 

一、預處理

預處理過程主要讀取c源程式,對偽指令和特殊符號進行處理。包括巨集,條件編譯,包含的標頭檔案,以及一些特殊符號。基本上是一個replace的過程。

gcc –E hello.c –o hello.i

以下為預處理後的輸出檔案hello.i的內容

# 1"hello.c"
# 1"<built-in>"
# 1"<command-line>"
# 1"hello.c"
# 1 "/usr/include/stdio.h"1 3 4
# 28"/usr/include/stdio.h" 3 4
/***** 省略了部分內容,包括stdio.h中的一些宣告及定義  *****/
# 2"hello.c" 2
int main()
{
 printf("Hello World\n");
 return 0;
}

預處理過程主要處理規則如下:

1、將所有的#define刪除,並且展開所有的巨集定義;

2、處理所有條件編譯指令,如#if,#ifdef等;

3、處理#include預編譯指令,將被包含的檔案插入到該預編譯指令的位置。該過程遞迴進行,及被包含的檔案可能還包含其他檔案。

4、刪除所有的註釋//和 /**/;

5、新增行號和檔案標識,如#2 “hello.c” 2,以便於編譯時編譯器產生除錯用的行號資訊及用於編譯時產生編譯錯誤或警告時能夠顯示行號資訊;

6、保留所有的#pragma編譯器指令,因為編譯器須要使用它們;

二、編譯

編譯過程通過詞法和語法分析,確認所有指令符合語法規則(否則報編譯錯),之後翻譯成對應的中間碼,在linux中被稱為RTL(Register Transfer Language),通常是平臺無關的,這個過程也被稱為編譯前端。編譯後端對RTL樹進行裁減,優化,得到在目標機上可執行的彙編程式碼。gcc採用as作為其彙編器,所以彙編碼是AT&T格式的,而不是Intel格式,所以在用gcc編譯嵌入式彙編時,也要採用AT&T格式。

gcc –S hello.i –o hello.s

以下為編譯後的輸出檔案hello.s的內容

    .file  "hello.c"
        .section    .rodata
.LC0:
        .string      "HelloWorld"
        .text
.globl main
        .type         main, @function
main:
        pushl         %ebp
        movl          %esp, %ebp
        andl $-16, %esp
        subl  $16, %esp
        movl          $.LC0, (%esp)
        call   puts
        movl          $0, %eax
        leave
        ret
        .size main, .-main
        .ident        "GCC: (GNU)4.4.0 20090506 (Red Hat 4.4.0-4)"
        .section   .note.GNU-stack,"",@progbits

三、彙編

彙編器是將彙編程式碼轉變成機器可以執行的命令,每一個彙編語句幾乎都對應一條機器指令。彙編相對於編譯過程比較簡單,根據彙編指令和機器指令的對照表一一翻譯即可。

 gcc –c hello.c –o hello.o

由於hello.o的內容為機器碼,不能以文字形式方便的呈現。

四、連結

連結器ld將各個目標檔案組裝在一起,解決符號依賴,庫依賴關係,並生成可執行檔案。

ld –static crt1.o crti.o crtbeginT.ohello.o –start-group –lgcc –lgcc_eh –lc-end-group crtend.o crtn.o

(省略了檔案的路徑名)。

當然連結的時候還會用到靜態連結庫,和動態連線庫。靜態庫和動態庫都是.o目標檔案的集合。

靜態庫是在連結過程中將相關程式碼提取出來加入可執行檔案的庫(即在連結的時候將函式的程式碼將從其所在地靜態連結庫中被拷貝到最終的可執行程式中),ar只是將一些別的檔案集合到一個檔案中。可以打包,當然也可以解包。

ar -v -q  test.a test.o

上面指令可以生成靜態連結庫test.a

動態庫在連結時只建立一些符號表,而在執行的時候才將有關庫的程式碼裝入記憶體,對映到執行時相應程序的虛地址空間。如果出錯,如找不到對應的.so檔案,會在執行的時候報動態連線錯(可用LD_LIBRARY_PATH指定路徑)。用file test.so可以看到test.so是shared object的ELF檔案。

gcc -sharedtest.so test.o

上面指令可以生成動態連線庫test.so

好了,整個編譯過程就如上所示了,那麼對於gcc還有一些編譯的選項的。具體如下:

GCC編譯選項

1. -c    

           編譯產生物件檔案(*.obj)而不連結成可執行檔案,當編譯幾個獨立的模組,而待以後由連結程式把它們連結在一起時,就可以使用這個選項,如:

         gcc -c hello.c ===> hello.o
         gcc hello.o

2. -o    

         允許使用者指定輸出檔名,如

           gcc hello.c -o hello.o
           or
           gcc hello.c -o hello
 

3. -g   

         指明編譯程式在編譯的輸出中應產生除錯資訊.這個除錯資訊使原始碼和變數名引用在除錯程式中或者當程式異常退出後在分析core檔案時可被使用.

4. -D  

       允許從編譯程式命令列定義巨集符號

    一共有兩種情況:一種是用-DMACRO,相當於在程式中使用#define MACRO,另一種是用-DMACRO=A,相當於程式中的#define MACRO A.如對下面這程式碼:

           #ifdef DEBUG
                printf("debugmessage\n");
           #endif

     編譯時可加上-DDEBUG引數,執行程式則打印出編譯資訊

5. -I  

         可指定查詢include檔案的其他位置.例如,如果有些include檔案位於比較特殊的地方,比如/usr/local/include,就可以增加此選項如下:

           gcc -c -I/usr/local/include -I/opt/include hello.c

        此時目錄搜尋會按給出的次序進行.

6. -E  

         這個選項是相對標準的,它允許修改命令列以使編譯程式把預先處理的C檔案發到標準輸出,而不實際編譯程式碼.在檢視C預處理偽指令和C巨集時,這是很有用的.可能的編譯輸出可重新定向到一個檔案,然後用編輯程式來分析:

           gcc -c -E hello.c >cpp.out   
   

     此命令使include檔案和程式被預先處理並重定向到檔案cpp.out.以後可以用編輯程或者分頁命令分析這個檔案,並確定最終的C語言程式碼看起來如何.

7. -O  

       優化選項,這個選項不是標準的

           -O和 -O1指定1級優化

           -O2 指定2級優化

           -O3 指定3級優化

           -O0指定不優化

           gcc -c O3 -O0 hello.c 

       當出現多個優化時,以最後一個為準!!

8. -Wall 

       以最高級別使用GNU編譯程式,專門用於顯示警告用!!

           gcc -Wall hello.c

9. -L

       指定連線庫的搜尋目錄,-l(小寫L)指定連線庫的名字

           gcc main.o -L/usr/lib -lqt -o hello

10.-share   

       此選項將盡量使用動態庫,所以生成檔案比較小,但是需要系統由動態庫

11.-static  

        此選項將禁止使用動態庫,所以,編譯出來的東西,一般都很大,也不需要什麼動態連線庫

12.-fPIC

        表示編譯為位置獨立的程式碼,不用此選項的話編譯後的程式碼是位置相關的所以動態載入時是通過程式碼拷貝的方式來滿足不同程序的需要,而不能達到真正程式碼段共享的目的。

相關推薦

一起cgcc編譯過程及其常用編譯選項

       上篇文章,知道了,C程式碼編譯後存放在記憶體中的位置,那麼C程式碼的整個編譯過程又是怎樣的呢?一條命令gcc hello.c就可以編譯成可執行程式a.out,然後./a.out之後就可以

一起演算法三分法求極值問題

          午後的陽光,那麼燦爛,如果不是溫度過高,那麼去西湖看看風景還是不錯的。想著,現在西湖邊應該是平靜的湖面,加上無數知了在柳枝上演奏著交響曲吧。小看了下非誠勿擾,那男生為了女孩唐靜付出了7年,唉,可是他錯了,女孩根本不愛他,不過期間他的執著和付出,很讓我感動

一起linuxwifi學習記錄

也差不多一個月沒有更新部落格了,元旦也過去了,8天的班也上完了,小病也好了,時間又回到了每個周的週末了,不知道幹嘛,突然發現好像失去了什麼,好像做了很多很多沒有意義的事,一直都是在原點打轉,不知道接下去會是什麼,不知道為了什麼。也許總會有一段日子覺得很迷茫,不知所措。這麼一

一起android4.0.3原始碼按鍵驅動短長按功能

第一:按鍵ADB除錯    通過使用adb shell getevent,可以得到如下裝置操作後的指令,具體表示的含義,可以參考網上很多的文章,這裡就不再敘述 這裡的0035和0036分別表示螢幕上的X座標和Y座標,後面的值表示具體的座標點

一起android4.0.3原始碼touchscreen配置+除錯記錄

        記得應該是上上週了,終於畢業了,離開了學校,就得面對現實的社會,以前學校實驗室裡,老師給了鑰匙,那電腦隨便用,那元器件隨便玩,什麼51微控制器啊,PIC微控制器啊,FPGA啊,arm11啊什麼的。想著做什麼就直接萬用版+電烙鐵什麼的一起搞定。除錯,寫程式,焊

一起android4.0.3原始碼wifi的簡單分析

關於wlan的組成   關於wifi應用層的介面的呼叫         首先從上層android wifi的應用開始,首先會根據android的wifimanager的類,例項化一個mwifimanager的物件,這個物件處理了所有wifi需要處理的任務,接著比如說開啟wi

cgcc編譯過程及其常用編譯選項

上篇文章,知道了,C程式碼編譯後存放在記憶體中的位置,那麼C程式碼的整個編譯過程又是怎樣的呢?一條命令gcc hello.c就可以編譯成可執行程式a.out,然後./a.out之後就可以執行hello.c這個程式的程式碼了。下面的文章分析的不錯,就整理了下。 hello.

跟我一起C++CC++(bool型別)

bool型別 C++新增型別,表示邏輯真與假 1.邏輯型也稱布林型,其取值為true(邏輯真)和false(邏輯假),儲存位元組數在不同編譯系統中可能有所不同,VC++中為1個位元組。 2.宣告方式:boolresult;result=true; 3.可以當作整數用(tru

跟我一起C++CC++(結構體記憶體對齊)

1.什麼是記憶體對齊 (1)      編譯器為每個“資料單元”按排在某個合適的位置上。 (2)      C、C++語言非常靈活,它允許你干涉“記憶體對齊”。也就是可以人為的設定編譯器的對齊方式。 2.為什麼要對齊 效能原因:在對齊的地址上訪問資料快。如果是位元組對齊方式

程式設計到大佬路:C語言程式(十二)

第十二天學習精要 遞迴初步 遞迴 一個函式,自己呼叫自己,就是遞迴。 # include <iostream> using namespace std; int factorial(int n) // 函式返回n的階乘 { if (n ==

程式設計到大佬路:C語言程式(五)

第五天學習精要 關係運算符和邏輯表示式 關係運算符 六種關係運算符用於數值的比較:相等 ==、不等 !=、大於 >、小於 <、大於等於 >=、小於等於 <=。 比較的結果是bool型別,成立則為true,反之為false。

程式設計到大佬路:C語言程式(六)

第六天學習精要 if語句 條件分支結構之if 語句 有時,並非所有的程式語句都要被順序執行到,會希望滿足某種條件就執行這部分語句,滿足另一條件就執行另一部分語句,這就需要“條件分支結構”。 依次計算表示式1、表示式2…只要碰到一個表示式i為真,則執行語

程式設計到大佬路:C語言程式(七)

第七天學習精要 for迴圈 for迴圈語句 for迴圈一般用於將某段程式碼(語句組)重複執行若干次。 第一步:計算“表示式1”。 第二步:計算“表示式2”,若其值為true,則執行“{ }”中的語句組,然後轉到第三步;若為false,則不再執行“{}”中的

程式設計到大佬路:C語言程式(八)

第八天學習精要 break語句和continue語句 break語句 可以出現在迴圈體中(for、 while、 do…while迴圈均可),其作用是跳出迴圈。 在多重迴圈的情況下,break語句只能跳出直接包含它的那一重迴圈。 例題:如果兩個不同

程式設計到大佬路:C語言程式特別篇(一)

程式設計習題 迴圈例題選講 例1.乘方計算 給出一個整數a和一個正整數n,求乘方ana^nan。 輸入:一行,包含兩個整數a和n。 -1000000 <= a <= 1000000,1 <= n <= 10000。 輸出:一個整數,

如何在WINFORM中彈出一個又控制元件的對話方塊 能給個簡單的示範C#程式碼嗎 以及一些小問題

如何在WINFORM中彈出一個又控制元件的對話方塊  能給個簡單的示範C#程式碼嗎順便問問 這段程式碼是啥意思啊        private void btn_catchMe_Click(object sender, System.EventArgs e)        {

一起AndroidToggleButtonSwitch

本文以一個簡單的小例子,簡述在Android開發中ToggleButton(開關按鈕)和Switch(開關)的簡單使用,僅供學習分享使用。 概述 ToggleButton是一個有兩種狀態(checked/unchecked)的按鈕,經常用於開/關等場景中,預設文字顯示(ON/OFF),Switch 是一個

Java美[從到高手演變]資料結構基礎、線性表、棧佇列、陣列字串

Java面試寶典之資料結構基礎 —— 線性表篇作者:egg郵箱:[email protected]這部分內容作為計算機專業最基礎的知識,幾乎被所有企業選中用來作考題,因此,本章我們從本章開始,我們將從基礎方面對資料結構進行講解,內容主要是線性表,包括棧、佇列、陣列、

學習IntelliJ IDEAMaven工程多模組繼承聚合建立(詳解)

一、前言IntelliJ IDEA開發環境搭建:具體參考《IntelliJ IDEA教程之如何配置Maven》Maven環境搭建:具體參考《Windows下Maven安裝以及配置》主要模擬企業開發是如何搭建Maven工程的,以Spring+SpringMVC+MyBatis為

C#編寫的區域網聊天工具(本Socket,拿來分享下~~~)

class Server //服務端方法 { #region 繫結IP、埠 IniOperate io = new IniOperate("C:\\lwj.ini"); public Socket Bind()