#########################text 1####################################
在爲Linux開發應用程序時,絕大多數情況下使用的都是C語言,因此幾乎每一位Linux程序員面臨的首要問題都是如何靈活運用C編譯器。目前Linux下最常用的C語言編譯器是GCC(GNU Compiler Collection),它是GNU項目中符合ANSI C標準的編譯系統,能夠編譯用C、C++和Object C等語言編寫的程序。GCC不僅功能非常強大,結構也異常靈活。最值得稱道的一點就是它可以通過不同的前端模塊來支持各種語言,如Java、Fortran、Pascal、Modula-3和Ada等。
int main(void)
{
printf ("Hello world, Linux programming!/n");
return 0;
}
然後執行下面的命令編譯和運行這段程序:
# ./hello
Hello world, Linux programming!
從程序員的角度看,只需簡單地執行一條GCC命令就可以了,但從編譯器的角度來看,卻需要完成一系列非常繁雜的工作。首先,GCC需要調用預處理程序cpp,由它負責展開在源文件中定義的宏,並向其中插入“#include”語句所包含的內容;接着,GCC會調用ccl和as將處理後的源代碼編譯成目標代碼;最後,GCC會調用鏈接程序ld,把生成的目標代碼鏈接成一個可執行程序。
此時若查看hello.cpp文件中的內容,會發現stdio.h的內容確實都插到文件裏去了,而其它應當被預處理的宏定義也都做了相應的處理。下一步是將hello.i編譯爲目標代碼,這可以通過使用-c參數來完成:
GCC默認將.i文件看成是預處理後的C語言源代碼,因此上述命令將自動跳過預處理步驟而開始執行編譯過程,也可以使用-x參數讓GCC從指定的步驟開始編譯。最後一步是將生成的目標文件鏈接成可執行文件:
在採用模塊化的設計思想進行軟件開發時,通常整個程序是由多個源文件組成的,相應地也就形成了多個編譯單元,使用GCC能夠很好地管理這些編譯單元。假設有一個由foo1.c和foo2.c兩個源文件組成的程序,爲了對它們進行編譯,並最終生成可執行程序foo,可以使用下面這條命令:
如果同時處理的文件不止一個,GCC仍然會按照預處理、編譯和鏈接的過程依次進行。如果深究起來,上面這條命令大致相當於依次執行如下三條命令:
# gcc -c foo2.c -o foo2.o
# gcc foo1.o foo2.o -o foo
在編譯一個包含許多源文件的工程時,若只用一條GCC命令來完成編譯是非常浪費時間的。假設項目中有100個源文件需要編譯,並且每個源文件中都包含10000行代碼,如果像上面那樣僅用一條GCC命令來完成編譯工作,那麼GCC需要將每個源文件都重新編譯一遍,然後再全部連接起來。很顯然,這樣浪費的時間相當多,尤其是當用戶只是修改了其中某一個文件的時候,完全沒有必要將每個文件都重新編譯一遍,因爲很多已經生成的目標文件是不會改變的。要解決這個問題,關鍵是要靈活運用GCC,同時還要藉助像Make這樣的工具。
void main(void)
{
long long int var = 1;
printf("It is not standard C code!/n");
}
下面來看看GCC是如何幫助程序員來發現這些錯誤的。當GCC在編譯不符合ANSI/ISO C語言標準的源代碼時,如果加上了-pedantic選項,那麼使用了擴展語法的地方將產生相應的警告信息:
illcode.c: In function `main':
illcode.c:9: ISO C89 does not support `long long'
illcode.c:8: return type of `main' is not `int'
需要注意的是,-pedantic編譯選項並不能保證被編譯程序與ANSI/ISO C標準的完全兼容,它僅僅只能用來幫助Linux程序員離這個目標越來越近。或者換句話說,-pedantic選項能夠幫助程序員發現一些不符合ANSI/ISO C標準的代碼,但不是全部,事實上只有ANSI/ISO C語言標準中要求進行編譯器診斷的那些情況,纔有可能被GCC發現並提出警告。
illcode.c:8: warning: return type of `main' is not `int'
illcode.c: In function `main':
illcode.c:9: warning: unused variable `var'
GCC給出的警告信息雖然從嚴格意義上說不能算作是錯誤,但卻很可能成爲錯誤的棲身之所。一個優秀的Linux程序員應該儘量避免產生警告信息,使自己的代碼始終保持簡潔、優美和健壯的特性。
cc1: warnings being treated as errors
illcode.c:8: warning: return type of `main' is not `int'
illcode.c: In function `main':
illcode.c:9: warning: unused variable `var'
對Linux程序員來講,GCC給出的警告信息是很有價值的,它們不僅可以幫助程序員寫出更加健壯的程序,而且還是跟蹤和調試程序的有力工具。建議在用GCC編譯源代碼時始終帶上-Wall選項,並把它逐漸培養成爲一種習慣,這對找出常見的隱式編程錯誤很有幫助。
同樣,如果使用了不在標準位置的庫文件,那麼可以通過-L選項向GCC的庫文件搜索路徑中添加新的目錄。例如,如果在/home/xiaowp/lib/目錄下有鏈接時所需要的庫文件libfoo.so,爲了讓GCC能夠順利地找到它,可以使用下面的命令:
值得好好解釋一下的是-l選項,它指示GCC去連接庫文件libfoo.so。Linux下的庫文件在命名時有一個約定,那就是應該以lib三個字母開頭,由於所有的庫文件都遵循了同樣的規範,因此在用-l選項指定鏈接的庫文件名時可以省去lib三個字母,也就是說GCC在對-lfoo進行處理時,會自動去鏈接名爲libfoo.so的文件。
###############################text3################################################
GCC for Win32 開發環境介紹(4)
第三章 GCC粗探——GCC的組成與附加參數
第一節 GCC的家族成員
總的來說,GCC應該是一個編譯器。可是,爲什麼我還要在這裏介紹GCC的家族成員呢?其實,整套的GCC環境並不是由GCC構成的,他是由多個包所組成的。這些包的互相作用產生了GCC的開發環境。其中,有一些包是你開發應用程序所必備的基本包。離開這些包你將無法正常使用GCC。
GCC的基本包列表。
GCC的基本開發環境,主要由一下幾個包構成。Binutils,這個是輔助GCC的工具包,裏面包含了連接器,彙編器,動態靜態庫生成程序,等等。GCC,這個包是GCC本身。當然GCC包中還包括一下幾個包,如core,java,ada等,每個包都代表了一種語言。然後,就是win32api,mingw-runtime,這個是在Win32下使用的標準函數包。如果,你使用的是Cygwin或者是在Unix環境下,那麼這個包就是GlibC。
所以,由上所述。GCC的基本包有:binutils gcc glibc/[win32api,mingw-runtime]有了這些包。你基本能夠開始編譯應用程序了。
當然,如果說你想要寫一個小程序。自然這些包已經夠了。但是如果你要寫一個較大的工程。那麼,這些包也許就不能很好的勝任你的工作了。因爲,對於一個大的項目,需要編譯的文件不只一個,而且還有依賴關係等等。
所以,GCC中還包括gmake包用於管理項目。當然,還有automake。但是我個人還是不太喜歡automake,automake其實是幫助你自動的管理你的項目,當然實現這個自動也是比較麻煩的,所以與其用automake管理中小型項目,不如用gmake自己寫個腳本。不過,automake通常用於源代碼發佈的應用,如果在開發時使用會延長開發週期。
Gmake,automake,都是通過編譯腳本來批量的編譯程序。他們能夠更具你所給定的依賴關係,來自動的判斷需要重新編譯的源代碼,然後編譯。這點的確可以幫助開發人員減輕不少的人力和開發週期。比如,你用Makefile管理一個項目,那麼在你第一次編譯程序以後,如果你的源代碼沒有做過任何編輯,那麼下次再調用gmake的程序時,gmake就不會再去一一編譯每個文件。而是簡單的連接一下主程序,或者什麼都不作的退出(這要取決於你寫的Makefile腳本)
但是,對於有些開發人員來說,上面這些包還是不能滿足他們的要求。因爲他們要調試程序。所以,GCC還包括另一個包。那就是GDB,gdb是GCC開發的,用於跟蹤調試的,命令符型調試器。它的功能還是比較強大的。基本,你能在VC下做到的,GDB也可以。不過,GDB的命令還是比較多的。掌握一些基本的調試命令一般就夠使用了。
總結
GCC開發環境包括如下幾大包。
binary |
基本包 |
提供基本的彙編器,連接器等 |
gcc |
基本包 |
各種語言的編譯器,包括C,C++,Ada,Java等 |
Win32api,mingwi-runtime/glibc |
基本包 |
系統函數庫 |
Gmake/automake |
需要包 |
管理項目編譯的程序 |
gdb |
附加包 |
調試程序 |
第二節 GCC的常用編譯參數
同VC,TC等編譯器不同,GCC其實是可以很方便的在提示符下編譯程序的。GCC在提示符下編譯程序,並沒有如同VC那樣的冗長而晦澀的編譯參數。相反,卻有着比VC更靈活且簡短的參數。
不得不承認,不懂GCC編譯參數的人,確實會損失一些GCC的強大功能。所以,我下面簡單介紹一下GCC的一些基本編譯參數。這裏,我以C編譯器爲例。
編譯二進制代碼
$gcc -c yours.c -o yours.o |
使用這段指令,GCC將會把yours.c編譯成yours.o的二進制代碼。其中,yours.o就類似於VC,TC中的.obj文檔。
編譯最簡單的小程序。
$gcc -o yours yours.c |
通過這條指令,GCC將會把yours.c源代碼編譯成名爲yours的可執行程序。當然,您也可以將yours.c改成我們剛纔介紹的yours.o文件。這樣,gcc將使用編譯剛纔編譯好的二進制文檔來鏈接程序。這裏,格式的特點是,-o 後面是一串文件列表,第一個參數是所編譯程序的文件名,從第二個開始,就是您編譯和連接該可執行程序所需要的二進制文檔或者源代碼。
編譯時將自己的頭文件目錄設爲默認頭文件目錄
$gcc -I”Your_Include_Files_Document_Path” -c yours.c -o yours.o |
這條指令中的-I參數將會把Your_Include_Files_Document_Path添加到你默認的頭文件目錄中。這樣您將可以使用 #include <your_include.h>來導入頭文件。
編譯時使用自己的靜態庫存放目錄
$gcc -L”Your_Lib_Files_Document_Path” -o yours yours.o |
這條指令將會讓GCC在連接時除了在默認Lib存放目錄中搜索指定的靜態庫以外,還會在Your_Lib_Files_Document_Path中搜索。
編譯時使用靜態連接庫
$gcc -lyour_lib -o yours yours.o |
這條指令將會讓GCC在連接時把 libyour_lib.a中您所用到的函數連接到可執行程序中。此處注意,GCC所使用的靜態連接庫是lib*.a格式的。在連接時,只且僅需要提供*的內容就可以了。
編譯時使用優化
$gcc -O2 -c yours.c -o yours.o |
使用優化方式編譯程序,其中除了-O2以外,還有-O3 -O1等等。他們代表不同的優化等級。最常用的,是-O2優化。當然,還有針對特殊CPU的優化,這裏就不介紹了。
編譯時顯示所有錯誤和警告信息
$gcc -Wall -c yours.c -o yours.o |
GCC在默認情況下,將對一些如變量申請未使用這樣的問題或者申請了沒有給予初始值的問題忽略。但是,如果使用了-Wall參數,編輯器將列出所有的警告信息。這樣,您就可以知道您的代碼中有多少可能會在其他操作系統下出錯的地方了。(用這個指令看看你的代碼有多少地方寫的不怎麼合適。)
編譯連接時,加入調試代碼
$gcc -g -o yours yours.c |
正如同VC有debug編譯模式一樣,GCC也有debug模式。添加了-g 參數編譯的可執行程序比普通程序略爲大一些,其中添加了一些調試代碼。這些代碼將被gdb所支持。
連接時縮小代碼體積
$gcc -s -o yours yours.o |
這個參數,似乎我沒有在Unix環境下看到過。也不知道具體什麼作用。因爲有人說Visual-MinGW生成的代碼小,於是研究了一下她的編譯參數,發現release模式的編譯參數就加了這一項。貌似編譯後的代碼的確縮小了很多。
獲得幫助
$gcc --help |
這條指令從意思上就能看出,獲得gcc的幫助信息。如果您有什麼特殊需要,也許這個指令能幫上點小忙。
第三節 如何寫一個簡單的Makefile
說了半天Makefile管理項目,我想現在該說一下如何寫了。其實,Makefile文件總體還是比較容易寫的,基本只要你會使用命令行,就可以寫Makefile。下面我簡單介紹一下Makefile的構成和書寫。
一個輸出 HelloWorld 的簡單Makefile
all: echo HelloWorld |
這個Makefile代碼,運行後將在屏幕上打印一個HelloWorld。all其實是類似C代碼中的main函數。gmake程序將在運行Makefile代碼時,首先運行此處的代碼。注意,此處echo前面的是<tab>。GCC對空格很敏感的。
添加依賴項的Makefile
all:depend @echo I am the main depend:closeecho @echo I am the depend closeecho: @echo off |
這個Makefile代碼,的作用還是輸出句子。不同的是她添加了幾個指令塊之間的依賴關係。其中all依賴於depend,depend依賴於closeecho。這樣,程序在編譯時將根據腳本的依賴關係來判斷文件編譯的先後順序。
執行Makefile
$make -f Makefile |
通常情況下,不用-f參數,make程序將在當前目錄下搜索名爲Makefile的文件作爲需要執行的文件。而使用-f將指定Makefile的文件名。
一個完整的Makefile
all:yours @echo OK yours:yours.o gcc -o yours yours.o yours.o:yours.c gcc -c yours.c -o yours.o |
更多有關Makefile的詳細內容請查相關資料。
文外音:
的確,發現了。我這個系列的文章進展的並沒有我預期和承諾的那麼快。確實挺讓我感到無奈的。不過,幸好你能夠理解我。總體來說,本文主要是簡單介紹一下GCC的一些基礎。因爲我發現有許多讀者對GCC並不是很瞭解。
說來也挺無奈的,最近發現原來周圍有個小人當道。此人姓名我也就隱去了。從去年開始,就常常在和我聊得比較好的幾個女生面前造我的謠。所以,原本應該9月該出來的文章拖延到了9月底。說起這個人,我希望大家要以此人爲鑑。不要像他那樣。
這人自稱在硬件方面很行,不過據我所知,他無非就是可以把各種型號的主板和硬件編號背出來而已。連基本的線程進程都搞不清楚。還成天鼓吹32bit的機器是淘汰的機器。不得不承認,這位滿口髒話爲人無恥的傢伙的確硬件方面挺不錯的。但是,我還是覺得做技術的人不要太追求表面的東西,不要追求過於超前的東西,同時也更要注意自己的品行道德。
也許是年紀大了,發了一些牢騷。希望各位不要建議。不過,事以至次,我也無力挽回了。
最後感謝各位對我的支持,更感謝那些願意爲我效力的朋友們。同時,如果您有什麼問題或者建議,都可以提出來。爲了您能及時準確的得到回覆請到http://blog.csdn.net/visioncat上發表你的留言。謝謝