1. 程式人生 > >c語言程式設計基礎------0.2GCC和GNU的關係,以及GCC的用法

c語言程式設計基礎------0.2GCC和GNU的關係,以及GCC的用法

1創作背景編輯

GCC(GNU Compiler Collection,GNU編譯器套件),是由 GNU 開發的程式語言編譯器。它是以GPL許可證所發行的自由軟體,也是 GNU計劃的關鍵部分。GCC原本作為GNU作業系統的官方編譯器,現已被大多數類Unix作業系統(如Linux、BSD、Mac OS X等)採納為標準的編譯器,GCC同樣適用於微軟的Windows。[2]GCC是自由軟體過程發展中的著名例子,由自由軟體基金會以GPL協議釋出。 GCC是大多數類Unix作業系統(如LinuxBSDMac OS X等)的標準的編譯器,GCC同樣適用於微軟的Windows。GCC支援多種計算機體系
晶片
,如x86ARM,並已移植到其他多種硬體平臺。 GCC 原名為 GNU C 語言編譯器(GNU C Compiler)[3],因為它原本只能處理C語言。GCC 很快地擴充套件,變得可處理C++。後來又擴充套件能夠支援更多程式語言,如FortranPascalObjective-CJavaAdaGo以及各類處理器架構上的組合語言等,所以改名GNU編譯器套件(GNU Compiler Collection)。[2]

2結構編輯

GCC的外部介面長得像一個標準的Unix編譯器。使用者在命令列下鍵入gcc之程式名,以及一些命令引數,以便決定每個輸入檔案使用的個別語言編譯器,併為輸出程式碼使用適合此硬體平臺的組合語言
編譯器
,並且選擇性地執行聯結器以製造可執行的程式。 每個語言編譯器都是獨立程式,此程式可處理輸入的原始碼,並輸出組合語言碼。全部的語言編譯器都擁有共通的中介架構:一個前端解析符合此語言的原始碼,併產生一抽象語法樹,以及一翻譯此語法樹成為GCC的暫存器轉換語言〈RTL〉的後端。編譯器最佳化與靜態程式碼解析技術(例如FORTIFY_SOURCE,一個試圖發現緩衝區溢位〈buffer overflow〉的編譯器)在此階段應用於程式碼上。最後,適用於此硬體架構的組合語言程式碼以Jack Davidson與Chris Fraser發明的演算法產出。 幾乎全部的GCC都由C寫成,除了Ada前端大部分以Ada寫成。

前端介面

前端的功能在於產生一個可讓後端處理之語法樹。此語法解析器是手寫之遞迴語法解析器。 直到2004年,程式的語法樹結構尚無法與欲產出的處理器架構脫鉤。而語法樹的規則有時在不同的語言前端也不一樣,有些前端會提供它們特別的語法樹規則。 在2005年,兩種與語言脫鉤的新型態語法樹納入GCC中。它們稱為GENERIC與GIMPLE。語法解析變成產生與語言相關的暫時語法樹,再將它們轉成GENERIC。之後再使用"gimplifier"技術降低GENERIC的複雜結構,成為一較簡單的靜態唯一形式(Static Single Assignment form,SSA)基礎的GIMPLE形式。此形式是一個與語言和處理器架構脫鉤的全域最佳化通用語言,適用於大多數的現代程式語言。

中介介面

一般編譯器作者會將語法樹的最佳化放在前端,但其實此步驟並不看語言的種類而有不同,且不需要用到語法解析器。因此GCC作者們將此步驟歸入通稱為中介階段的部分裡。此類的最佳化包括消解死碼、消解重複運算與全域數值重編碼等。許多最佳化技巧也正在實作中。

後端介面

GCC後端的行為因不同的前處理器巨集和特定架構的功能而不同,例如不同的字元尺寸、呼叫方式與大小尾序等。後端介面的前半部利用這些訊息決定其RTL的生成形式,因此雖然GCC的RTL理論上不受處理器影響,但在此階段其抽象指令已被轉換成目標架構的格式。 GCC的最佳化技巧依其釋出版本而有很大不同,但都包含了標準的最佳化演算法,例如迴圈最佳化、執行緒跳躍、共通程式子句消減、指令排程等等。而RTL的最佳化由於可用的情形較少,且缺乏較高階的資訊,因此相比較起來,增加的GIMPLE語法樹形式,便顯得比較不重要。 後端經由一次重讀取步驟後,利用描述目標處理器的指令集時所取得的資訊,將抽象暫存器替換成處理器的真實暫存器。此階段非常複雜,因為它必須關注所有GCC可移植平臺的處理器指令集的規格與技術細節。 後端的最後步驟相當公式化,僅僅將前一階段得到的組合語言程式碼藉由簡單的子例程轉換其暫存器與記憶體位置成相對應的機器碼

3基本用法編輯

在使用GCC編譯器的時候,我們必須給出一系列必要的呼叫引數和檔名稱。GCC編譯器的呼叫引數大約有100多個,這裡只介紹其中最基本、最常用的引數。具體可參考GCC Manual。 GCC最基本的用法是∶gcc [options] [filenames] 其中options就是編譯器所需要的引數,filenames給出相關的檔名稱。 -c,只編譯,不連結成為可執行檔案編譯器只是由輸入的.c等原始碼檔案生成.o為字尾的目標檔案,通常用於編譯不包含主程式的子程式檔案。 -o output_filename,確定輸出檔案的名稱為output_filename,同時這個名稱不能和原始檔同名。如果不給出這個選項,gcc就給出預設的可執行檔案a.out。 -g,產生符號除錯工具(GNU的gdb)所必要的符號資訊,要想對原始碼進行除錯,我們就必須加入這個選項。 -O,對程式進行優化編譯、連結,採用這個選項,整個原始碼會在編譯、連結過程中進行優化處理,這樣產生的可執行檔案的執行效率可以提高,但是,編譯、連結的速度就相應地要慢一些。 -O2,比-O更好的優化編譯、連結,當然整個編譯、連結過程會更慢。 -Idirname,將dirname所指出的目錄加入到程式標頭檔案目錄列表中,是在預編譯過程中使用的引數。C程式中的標頭檔案包含兩種情況∶ A)#include <myinc.h> B)#include “myinc.h” 其中,A類使用尖括號(< >),B類使用雙引號(“ ”)。對於A類,預處理程式cpp在系統預設包含檔案目錄(如/usr/include)中搜尋相應的檔案,而B類,預處理程式在目標檔案的資料夾內搜尋相應檔案。 -v gcc執行時執行的詳細過程,gcc及其相關程式的版本號 原版gcc manual該選項英文解釋 Print (on standard error output) the commands executed to run the stages of compilation. Also print the version number of the compiler driver program and of the preprocessor and the compiler proper. 編譯程式時加上該選項可以看到gcc搜尋標頭檔案/庫檔案時使用的搜尋路徑!

4基本規則編輯

gcc所遵循的部分約定規則: .c為字尾的檔案,C語言原始碼檔案; .a為字尾的檔案,是由目標檔案構成的檔案庫檔案; .C,.cc或.cxx 為字尾的檔案,是C++原始碼檔案且必須要經過預處理; .h為字尾的檔案,是程式所包含的標頭檔案; .i 為字尾的檔案,是C原始碼檔案且不應該對其執行預處理; .ii為字尾的檔案,是C++原始碼檔案且不應該對其執行預處理; .m為字尾的檔案,是Objective-C原始碼檔案; .mm為字尾的檔案是Objective-C++原始碼檔案; .o為字尾的檔案,是編譯後的目標檔案; .s為字尾的檔案,是彙編語言原始碼檔案; .S為字尾的檔案,是經過預編譯彙編語言原始碼檔案。

5語言支援編輯

以2006年5月24日釋出的4.1.1版為準,本編譯器版本可處理下列語言: Ada 〈GNAT〉 C 〈GCC〉 C++(G++) Fortran 〈Fortran 77: G77, Fortran 90: GFORTRAN〉 Java編譯器:GCJ;直譯器:GIJ〉 Objective-C++ 先前版本納入的CHILL前端由於缺乏維護而被廢棄。 Fortran前端在4.0版之前是G77,此前端僅支援Fortran 77。在本版本中,G77被廢棄而採用更新的GFortran,因為此前端支援Fortran 95。 下列前端依然存在: Modula-2 Modula-3 PL/I D語言 Mercury VHDL

6執行過程編輯

雖然我們稱GCC是C語言編譯器,但使用gcc由C語言原始碼檔案生成可執行檔案的過程不僅僅是編譯的過程,而是要經歷四個相互關聯的步驟∶預處理(也稱預編譯,Preprocessing)、編譯(Compilation)、彙編(Assembly)和連線(Linking)。 命令gcc首先呼叫cpp進行預處理,在預處理過程中,對原始碼檔案中的檔案包含(include)、預編譯語句(如巨集定義define等)進行分析。接著呼叫cc1進行編譯,這個階段根據輸入檔案生成以.o為字尾的目標檔案。彙編過程是針對組合語言的步驟,呼叫as進行工作,一般來講,.S為字尾的組合語言原始碼檔案和彙編、.s為字尾的組合語言檔案經過預編譯彙編之後都生成以.o為字尾的目標檔案。當所有的目標檔案都生成之後,gcc就呼叫ld來完成最後的關鍵性工作,這個階段就是連線。在連線階段,所有的目標檔案被安排在可執行程式中的恰當的位置,同時,該程式所呼叫到的庫函式也從各自所在的檔案庫中連到合適的地方。

7執行過程示例編輯

  • 示例程式碼
1 2 3 4 5 6 7 #include<stdio.h> intmain(void) { printf("hello\n"); return0; }
這個過程處理巨集定義和include,並做語法檢查。 可以看到預編譯後,程式碼從6行擴充套件到了910行。
1 2 3 4 5 gcc-Ea.c-oa.i cata.c|wc-l 5 cata.i|wc-l 910
  • 編譯過程
這個階段,生成彙編程式碼。
1 2 3 gcc-Sa.i-oa.s cata.s|wc-l 59
這個階段,生成目的碼。 此過程生成ELF格式的目的碼
1 2 3 gcc-ca.s-oa.o filea.o a.o:ELF64-bitLSBrelocatable,AMDx86-64,version1(SYSV),notstripped
連結過程。生成可執行程式碼。連結分為兩種,一種是靜態連結,另外一種是動態連結。使用靜態連結的好處是,依賴的動態連結庫較少,對動態連結庫的版本不會很敏感,具有較好的相容性;缺點是生成的程式比較大。使用動態連結的好處是,生成的程式比較小,佔用較少的記憶體。
1 gcca.o-oa
程式執行:
1 2 ./a hello

8處理器架構編輯

GCC4.1支援下列處理器架構: Alpha ARM Atmel AVR Blackfin H8/300 IA-32〈x86〉 與x86-64 MorphoSys 家族 Motorola 88000 MIPS System/370,System/390 SuperH HC12 SPARC VAX Renesas R8C/M16C/M32C家族 較不知名的處理器架構也在官方釋出版本中支援: A29K ARC C4x CRIS D30V DSP16xx FR-30 FR-V Intel i960 IP2000 M32R 68HC11 MCORE MMIX MN10200 MN10300 NS32K ROMP Stormy16 V850 Xtensa 由FSF個別維護的GCC處理器架構: D10V MicroBlaze PDP-10 MSP430 Z8000 當GCC需要移植到一個新平臺上,通常使用此平臺固有的語言來撰寫其初始階段。

9程式除錯編輯

為 GCC 除錯的首選工具當然是 GNU 除錯器。其他特殊用途的除錯工具是 Valgrind, 用以發現記憶體漏失 (Memory leak)。而 GNU 測量器 (gprof) 可以得知程式中某些函式花費多少時間,以及其呼叫頻率;此功能需要使用者在編譯時選定測量〈profiling〉選項。

10使用技巧編輯

首先檢查是否在你的機器上安裝了GCC,使用命令: 可用rpm -q gcc 檢查。 如果沒有安裝,請依序檢查並安裝下面各RPM libbinutils binutils make glibc-devel gcc-cpp gcc 看下面的例子:test.c
1 2 3 4 5 6 7 8 #include<stdio.h> intmain() { char*str="IlikeLinux!IadvicesyoujoinintheLinuxWorld"; printf("%s",str); return0; }
使用gcc編譯。輸入gcc -c test.c得到目標檔案test.o.-c命令表示對檔案進行編譯和彙編。但並不連線。如果再鍵入gcc -o ../bin/test test.o,那麼將得到名為test的可執行檔案。其實這兩步可以一氣呵成,gcc ../bin/test test.c.如果程式沒有錯誤就生成了可執行檔案。也許你會覺得基於命令列的編譯器比不上如VC之類的整合開發環境,的確gcc的介面要改進,但是你一旦熟練了就會感到。gcc的效率如此之高。可以告訴大家的是Linux底下強大的C/C++整合開發環境Kdevelop和Vc一樣強大,使用了Gcc編譯器。 GNU C編譯器 即gcc是一個功能強大的ANSI C相容編譯器,你會操作其他作業系統下的一種C編譯器,能很快掌握GCC. 1、使用Gcc,Gcc是基於命令列的,使用時通常後跟一些選項和檔名。Gcc的基本用法如下: gcc [options] [filenames] 命令列選項制定操作將對命令列上的每個給出的檔案執行。 2、GCC的常用選項 編譯選項:gcc有超過100個的編譯選項可用。具體的可以使用命令man gcc察看 優化選項:用GCC編譯C/C++程式碼時,它會試著用最少的時間完成編譯並且編譯後的程式碼易於除錯。易於除錯意味著編譯後的程式碼與原始碼有同樣的執行順序,編譯後的程式碼沒有經過優化。有很多的選項可以告訴GCC在耗費更多編譯時間和犧牲易除錯性的基礎上產生更小更快的可執行檔案。這些選項中最典型的就是-O和-O2。-O選項告訴gcc對原始碼進行基本優化。-O2選項告訴GCC產生儘可能小的和儘可能快的程式碼。還有一些很特殊的選項可以通過man gcc察看。 除錯和剖析選項:GCC支援數種除錯剖析選項。在這些選項中最常用的是-g和-pg.-g選項告訴gcc產生能被GNU偵錯程式(如gdb)使用的除錯資訊,以便除錯使用者的程式。-pg選項告訴gcc在使用者的程式中加入額外的程式碼,執行時,產生gprof用的剖析資訊以顯示程式的耗時情況。 3、使用gdb 使用方法:在命令列中鍵入gdb並按回車就可以執行gdb了,啟動gdb後,能在命令列上制定很多的選項,也可以下面的方式來執行gdb: gdb filename 用這種方式執行gdb時,能直接指定想要除錯的程式。在命令列上健入gdb -h得到一個有關gdb的選項的說明簡單列表。 編譯程式碼以供除錯,為了使gdb工作,必須使程式在編譯時包含除錯資訊,除錯資訊包含程式裡的每個變數的型別,在可執行檔案裡的地址對映以及原始碼行號。gdb利用這些資訊使原始碼機器碼相關聯。 關於gcc的大體就寫這麼多吧,更多的資訊可以查詢幫助,記得學習Linux的一大武器man或者info命令,下次在介紹一下使用C/C++編寫大型程式的makefile檔案和make命令。

11版本釋出編輯

2012年03月23日,GCC 首個公開發布版本是在 1987 年由 Richard Stallman 釋出的,到今天已經整整 25 年了。為了慶祝 25 週年,GCC 也相應釋出了 GCC 4.7.0 版本,這是 GCC 一個全新的重要版本。 GCC 4.7.0 帶來了一組關於連結時優化 (LTO) 框架可提升伸縮性和降低記憶體使用,據開發者稱,在 64 位系統上需要 8G 記憶體來對 Firefox 進行優化,而是用了 LTO 後只需 3G。 此外就是體驗的支援軟體事務記憶體,支援更多 C++11 標準,包括原子性、C++11 記憶體模型,使用者定義文字、別名宣告、構造器委派和可擴充套件的語法等。 GCC 4.7.0 還改進對 Fortran 的支援,支援 Google Go 1 等等多項改進。[4] 2012年06月14日,GCC 4.7.1 釋出了,這是一個 bug 修復版本,主要是 4.7.0 中的一些迴歸測試發現的問題修復。[5] 2013年04月11日,GCC 4.7.3 釋出。 2013年03月22日,GCC 4.8.0釋出,進一步加強了對已C++11的支援。並且G++開始支援-std=c++1y選項,用來支援計劃於2014年釋出的C++11修訂版標準(C++14)。[6] 2013年10月16日,GCC 4.8.2釋出。提供了對於OpenMP 4.0的支援。 2014年04月22日,GCC釋出了4.9.0版本,提供了對C11標準的Generic Selection語法特性(_Generic)的支援以及對多執行緒方面特性的支援。

12linux中安裝編輯

RedHat中安裝

用which gcc命令檢視,假如有顯示” /usr/bin/gcc”的話說明已經安裝了,否則就是沒有安裝。 這裡以redhat 64位 linux為例。首先將redhat的iso系統映象掛在到/mnt/cdrom目錄下:
  mount -o loop /data/rhel-server-5.4-x86_64-dvd.iso /mnt/cdrom
  進入/mnt/cdrom目錄,就可以訪問iso映象中的內容了。
  cd /mnt/cdrom
  cd Server/ 安裝gcc的依賴包以及gcc,按以下命令依次執行:
  rpm -ivh binutils-2.17.50.0.6-12.el5.x86_64.rpm
  rpm -ivh cpp-4.1.2-46.el5.x86_64.rpm
  rpm -ivh kernel-headers-2.6.18-164.el5.x86_64.rpm
  rpm -ivh glibc-devel-2.5-42.x86_64.rpm
  rpm -ivh glibc-headers-2.5-42.x86_64.rpm
  rpm -ivh libgomp-4.4.0-6.el5.x86_64.rpm
  rpm -ivh gcc-4.1.2-46.el5.x86_64.rpm
  大家在安裝rpm包時,由於依賴關係,在安裝時會提示“此包依賴其他包xx”,讓你先安裝其他包,總之大家按照提示來,提示你先安裝哪個包就安裝哪個包。

Ubuntu中安裝

安裝4.8版為例: sudo apt-get install gcc-4.8