?? ? ? ?這是一份非常簡短的文檔,可以幫助你熟悉UNIX系統(tǒng)上C編程環(huán)境的基礎(chǔ)知識竿屹。它不是面面俱到或特別詳細报强,只是給你足夠的知識讓你繼續(xù)學(xué)習(xí)。
? ? ? 關(guān)于編程的幾點一般建議:如果想成為一名專業(yè)程序員拱燃,需要掌握的不僅僅是語言的語法秉溉。具體來說,應(yīng)該了解你的工具,了解你的庫召嘶,并了解你的文檔父晶。與C編譯相關(guān)的工具是gcc、gdb和ld弄跌。還有大量的庫函數(shù)也可供你使用甲喝,但幸運的是libc包含了許多功能,默認情況下它與所有C程序相關(guān)聯(lián)——需要做的就是包含正確的頭文件碟绑。最后俺猿,了解如何找到所需的庫函數(shù)(例如,學(xué)習(xí)查找和閱讀手冊頁)是一項值得掌握的技能格仲。我們稍后將更詳細地討論這些內(nèi)容押袍。
? ? ? 就像生活中(幾乎)所有值得做的事情,成為這些領(lǐng)域的專家需要時間——事先花時間了解有關(guān)工具和環(huán)境的更多信息凯肋,絕對值得付出努力谊惭。
? ? ? E.1 一個簡單的C程序
? ? ? 我們從一個簡單的C程序開始,它保存在文件“hw.c”中侮东。與Java不同圈盔,文件名和文件內(nèi)容之間不一定有關(guān)系。因此悄雅,請以適當(dāng)?shù)姆绞角茫媚愕某WR來命名文件。
? ? ? 第一行指定要包含的文件宽闲,在本例中為stdio.h众眨,它包含許多常用輸入/輸出函數(shù)的“原型”。我們感興趣的是printf()容诬。當(dāng)你使用#include指令時娩梨,就告訴C預(yù)處理器(cpp)查找特定文件(例如,stdio.h)览徒,并將其直接插入到#include的代碼中狈定。默認情況下,cpp將查看目錄/usr/include/习蓬,嘗試查找該文件纽什。
? ? ? 下面一部分指定main()函數(shù)的簽名,即它返回一個整數(shù)(int)友雳,并用兩個參數(shù)來調(diào)用稿湿,一個整數(shù)argc,它是命令行上參數(shù)數(shù)量的計數(shù)押赊。一個指向字符(argv)的指針數(shù)組,每個指針都包含命令行中的一個單詞,最后一個單詞為null流礁。下面的指針和數(shù)組會更多涕俗。
? /* header files go up here */
? /* note that C comments are enclosed within a slash and a star, and
? may wrap over lines */
? // if you use gcc, two slashes will work too (and may be preferred)
? #include <stdio.h>
? /* main returns an integer */
? int main(int argc, char *argv[]) {
? /* printf is our output function;
? by default, writes to standard out */
? /* printf returns an integer, but we ignore that */
? printf("hello, world\n");
? /* return 0 to indicate all went well */
? return(0);
? }
? ? ? 程序然后簡單打印字符串“hello,world”神帅,并將輸出流換到下一行再姑,這是由printf()調(diào)用結(jié)束時的“\n”實現(xiàn)的。然后找御,程序返回一個值并結(jié)束元镀,該值被傳遞回執(zhí)行程序的shell。終端上的腳本或用戶可以檢查此值(在csh和tcsh shell中霎桅,它存儲在狀態(tài)變量中)栖疑,以查看程序是干凈地退出還是出錯。
? ? ? E.2 編譯和執(zhí)行
? ? ? 我們現(xiàn)在將學(xué)習(xí)如何編譯程序滔驶。請注意遇革,我們將使用gcc作為示例,但在某些平臺上揭糕,可以使用不同的(本機)編譯器cc萝快。
? ? ? 在shell提示符下,只需鍵入:
? prompt> gcc hw.c
? gcc不是真正的編譯器著角,而是所謂的“編譯器驅(qū)動程序”揪漩,因此它協(xié)調(diào)了編譯的許多步驟。通常有4~5個步驟吏口。首先奄容,gcc將執(zhí)行cpp(C預(yù)處理器)來處理某些指令(例如#define和#include。程序cpp只是一個源到源的轉(zhuǎn)換器锨侯,所以它的最終產(chǎn)品仍然只是源代碼( 即一個C文件)嫩海。然后真正的編譯將開始,通常是一個名為cc1的命令囚痴。這會將源代碼級別的C代碼轉(zhuǎn)換為特定主機的低級匯編代碼叁怪。然后執(zhí)行匯編程序as,生成目標代碼(機器可以真正理解的數(shù)據(jù)位和代碼位)深滚,最后鏈接編輯器(或鏈接器)ld將它們組合成最終的可執(zhí)行程序奕谭。幸運的是(!)痴荐,在大多數(shù)情況下血柳,你可以不明白gcc如何工作,只需愉快地使用正確的標志生兆。
? ? ? 上面編譯的結(jié)果是一個可執(zhí)行文件难捌,命名為(默認情況下)a.out。然后運行該程序,只需鍵入:
? prompt> ./a.out
? ? ? 運行該程序時根吁,操作系統(tǒng)將正確設(shè)置argc和argv员淫,以便程序可以根據(jù)需要處理命令行參數(shù)。具體來說击敌,argc將等于1介返,argv [0]將是字符串“./a.out”,而argv[1]將是null沃斤,表示數(shù)組的結(jié)束圣蝎。
? ? ? E.3 有用的標志
? ? ? 在繼續(xù)使用C語言之前,我們首先指出一些有用的gcc編譯標志衡瓶。
? prompt> gcc -o hw hw.c # -o: to specify the executable name
? prompt> gcc -Wall hw.c # -Wall: gives much better warnings
? prompt> gcc -g hw.c # -g: to enable debugging with gdb
? prompt> gcc -O hw.c # -O: to turn on optimization
? ? ? 當(dāng)然徘公,你可以根據(jù)需要組合這些標志(例如gcc -o hw -g -Wall hw.c)。在這些標志中鞍陨,你應(yīng)該總是使用-Wall步淹,這會提供很多關(guān)于可能出錯的額外警告。不要忽視警告诚撵!相反缭裆,要修復(fù)它們,讓它們幸福地消失寿烟。
? ? ? E.4 與庫鏈接
? ? ? 有時澈驼,你可能想在程序中使用庫函數(shù)。因為C庫中有很多函數(shù)(可以自動鏈接到每個程序)筛武,所以通常要做的就是找到正確的#include文件缝其。最好的方法是通過手冊頁(manual page),通常稱為man page徘六。
? ? ? 例如内边,假設(shè)你要使用fork()系統(tǒng)調(diào)用[1]。在shell提示符下輸入man fork待锈,你將獲得fork()如何工作的文本描述漠其。最頂部的是一個簡短的代碼片段,它告訴你在程序中需要#include哪些文件才能讓它通過編譯竿音。對于fork()和屎,需要#include sys/types.h和unistd.h,按如下方式完成:
? #include <sys/types.h>
? #include <unistd.h>
? ? ? 但是春瞬,某些庫函數(shù)不在C庫中柴信,因此你必須做更多的工作。例如宽气,數(shù)學(xué)庫有許多有用的函數(shù)(正弦随常、余弦潜沦、正切等)。如果要在代碼中包含函數(shù)tan()线罕,應(yīng)該先查手冊頁止潮。在tan的Linux手冊頁的頂部窃判,你會看到以下兩行代碼:
? #include <math.h>
? ...
? Link with -lm.
? ? 你已經(jīng)應(yīng)該理解了第一行——你需要#include數(shù)學(xué)庫钞楼,它位于文件系統(tǒng)的標準位置(即/usr/include/math.h)。但是袄琳,下一行告訴你如何將程序與數(shù)學(xué)庫“鏈接”询件。有許多有用的庫可以鏈接。其中許多都放在/usr/lib中唆樊,數(shù)學(xué)庫也確實在這里宛琅。
? ? ? 有兩種類型的庫:靜態(tài)鏈接庫(以.a結(jié)尾)和動態(tài)鏈接庫(以.so結(jié)尾)。靜態(tài)鏈接庫直接組合到可執(zhí)行文件中逗旁。也就是說嘿辟,鏈接器將庫的低級代碼插入到可執(zhí)行文件中,從而產(chǎn)生更大的二進制對象片效。動態(tài)鏈接通過在程序可執(zhí)行文件中包含對庫的引用來改進這一點红伦。程序運行時,操作系統(tǒng)加載程序動態(tài)鏈接庫淀衣。這種方法優(yōu)于靜態(tài)方法昙读,因為它節(jié)省了磁盤空間(沒有不必要的大型可執(zhí)行文件),并允許應(yīng)用程序在內(nèi)存中共享庫代碼和靜態(tài)數(shù)據(jù)膨桥。對于數(shù)學(xué)庫蛮浑,靜態(tài)和動態(tài)版本都可用,靜態(tài)版本是/usr/lib/libm.a只嚣,動態(tài)版本是/usr/lib/libm.so沮稚。小編是一個有著10年開發(fā)經(jīng)驗的C++程序員,關(guān)于C++册舞,自己有做材料的整合蕴掏,一個完整的學(xué)習(xí)C++的路線,學(xué)習(xí)材料和工具給大家环础!希望你也能憑自己的努力囚似,成為下一個優(yōu)秀的程序員。
? ? ? 在任何情況下线得,要與數(shù)學(xué)庫鏈接饶唤,都需要向鏈接編輯器指定庫。這可以通過使用正確的標志調(diào)用gcc來實現(xiàn)贯钩。
? prompt> gcc -o hw hw.c -Wall -lm
? ? ? -l×××標志告訴鏈接器查找lib×××.so或lib×××.a募狂,可能按此順序办素。如果出于某種原因,你堅持使用動態(tài)庫而不是靜態(tài)庫祸穷,那么可以使用另一個標志——看看你是否能找到它是什么性穿。人們有時更喜歡庫的靜態(tài)版本,因為使用動態(tài)庫有一點點性能開銷雷滚。
? ? ? 最后要注意:如果你希望編譯器在不同于常用位置的路徑中搜索頭文件需曾,或者希望它與你指定的庫鏈接,可以使用編譯器標志-I /foo/bar祈远,來查找目錄/foo/ bar中的頭文件呆万,使用-L /foo/bar標志來查找/foo/bar目錄中的庫。以這種方式指定的一個常用目錄是“.”(稱為“點”)车份,它是UNIX中當(dāng)前目錄的簡寫谋减。
? ? ? 請注意,-I標志應(yīng)該針對編譯扫沼,而-L標志針對鏈接出爹。
? ? ? E.5 分別編譯
? ? ? 一旦程序開始變得足夠大,你可能希望將其拆分為單獨的文件缎除,分別編譯每個文件严就,然后將它們鏈接在一起。例如伴找,假設(shè)你有兩個文件盈蛮,hw.c和helper.c,希望單獨編譯它們技矮,然后將它們鏈接在一起抖誉。
? # we are using -Wall for warnings, -O for optimization
? prompt> gcc -Wall -O -c hw.c
? prompt> gcc -Wall -O -c helper.c
? prompt> gcc -o hw hw.o helper.o -lm
? ? ? -c標志告訴編譯器只是生成一個目標文件——在本例中是名為hw.o和helper.o的文件。這些文件不是可執(zhí)行文件衰倦,而只是每個源文件中代碼的機器代碼表示袒炉。要將目標文件組合成可執(zhí)行文件,必須將它們“鏈接”在一起樊零。這是通過第三行g(shù)cc -o hw hw.o helper.o)完成的我磁。在這種情況下,gcc看到指定的輸入文件不是源文件(.c)驻襟,而是目標文件(.o)夺艰,因此直接跳到最后一步,調(diào)用鏈接編輯器ld將它們鏈接到一起沉衣,得到單個可執(zhí)行文件郁副。由于它的功能,這行通常被稱為“鏈接行”豌习,并且可以指定特定的鏈接命令存谎,例如-lm拔疚。類似地,僅在編譯階段需要的標志既荚,諸如-Wall和-O稚失,就不需要包含在鏈接行上,只是包含在編譯行上恰聘。
? ? ? 當(dāng)然句各,你可以在一行中為gcc指定所有C源文件(gcc -Wall -O -o hw hw.c helper.c),但這需要系統(tǒng)重新編譯每個源代碼文件憨琳,這個過程可能很耗時诫钓。通過單獨編譯每個源文件,你只需重新編譯編輯修改過的文件篙螟,從而節(jié)省時間,提高工作效率问拘。這個過程最好由另一個程序make來管理遍略,我們接下來介紹它。
? ? ? E.6 Makefile文件
? ? ? 程序make讓你自動化大部分構(gòu)建過程骤坐,因此對于任何認真的程序(和程序員)來說绪杏,都是一個至關(guān)重要的工具。來看一個簡單的例子纽绍,它保存在名為Makefile的文件蕾久。
? ? ? 要構(gòu)建程序,只需輸入:
? prompt> make
? ? ? 這會(默認)查找Makefile或makefile拌夏,將其作為輸入(你可以用標志指定不同的makefile僧著,閱讀手冊頁,找出是哪個標志)障簿。gmake是make的gnu版本盹愚,比傳統(tǒng)的make功能更多,所以我們將在下面的部分中重點介紹它(盡管我們互換使用這兩個詞)站故。這些討論大多數(shù)都基于gmake的info頁面皆怕,要了解如何查找這些頁面,請參閱“E.8文檔”部分西篓。另外請注意:在Linux系統(tǒng)上愈腾,gmake和make是一樣的。
? hw: hw.o helper.o
? gcc -o hw hw.o helper.o -lm
? hw.o: hw.c
? gcc -O -Wall -c hw.c
? helper.o: helper.c
? gcc -O -Wall -c helper.c
? clean:
? rm -f hw.o helper.o hw
? ? ? Makefile基于規(guī)則岂津,這些規(guī)則決定需要發(fā)生的事情虱黄。規(guī)則的一般形式是:
? target: prerequisite1 prerequisite2 ...
? command1
? command2
? ...
? ? ? target(目標)通常是程序生成的文件的名稱。目標的例子是可執(zhí)行文件或目標文件寸爆。目標也可以是要執(zhí)行的操作的名稱礁鲁,例如在我們的示例中為“clean”盐欺。
? ? ? prerequisite(先決條件)是用于生成目標的輸入文件。目標通常依賴于幾個文件仅醇。例如冗美,要構(gòu)建可執(zhí)行文件hw,需要首先構(gòu)建兩個目標文件:hw.o和helper.o析二。
? ? ? 最后粉洼,command(命令)是一個執(zhí)行的動作。一條規(guī)則可能有多個命令叶摄,每個命令都在自己的行上属韧。重要提示:必須在每個命令行的開頭放一個制表符!如果你只放空格蛤吓,make會打印出一些含糊的錯誤信息并退出宵喂。
? ? ? 通常,命令在具有先決條件的規(guī)則中会傲,如果任何先決條件發(fā)生更改锅棕,就要重新創(chuàng)建目標文件。但是淌山,為目標指定命令的規(guī)則不需要先決條件裸燎。例如,包含delete命令泼疑、與目標“clean”相關(guān)的規(guī)則中德绿,沒有先決條件。
? ? ? 回到我們的例子退渗,在執(zhí)行make時移稳,大致工作如下:首先,看到目標hw氓辣,并且意識到要構(gòu)建它秒裕,它必須具備兩個先決條件,hw.o和helper.o钞啸。因此几蜻,hw依賴于這兩個目標文件。然后体斩,Make將檢查每個目標梭稚。在檢查hw.o時,看到它取決于hw.c絮吵。這是關(guān)鍵:如果hw.c最近被修改弧烤,但hw.o沒有被創(chuàng)建,make會知道hw.o已經(jīng)過時蹬敲,應(yīng)該重新生成暇昂。在這種情況下莺戒,會執(zhí)行命令行g(shù)cc -O -Wall -c hw.c,生成hw.o急波。因此从铲,如果你正在編譯大型程序,make會知道哪些目標文件需要根據(jù)其依賴項重新生成澄暮,并且只會執(zhí)行必要的工作來重新創(chuàng)建可執(zhí)行文件名段。另外請注意,如果hw.o根本不存在泣懊,也會被創(chuàng)建伸辟。
? ? ? 繼續(xù),基于上面定義的相同標準馍刮,helper.o也會重新生成或創(chuàng)建信夫。當(dāng)兩個目標文件都已創(chuàng)建時,make現(xiàn)在可以執(zhí)行命令來創(chuàng)建最終的可執(zhí)行文件渠退,然后返回并執(zhí)行以下操作:gcc -o hw hw.o helper.o -lm忙迁。
? ? ? 到目前為止,我們一直沒提makefile中的clean目標碎乃。
? ? ? 要使用它,必須明確提出要求惠奸,鍵入以下代碼:
? prompt> make clean
? ? ? 這會在命令行上執(zhí)行該命令梅誓。因為clean目標沒有先決條件,所以輸入make clean將導(dǎo)致命令被執(zhí)行佛南。在這種情況下梗掰,clean目標用于刪除目標文件和可執(zhí)行文件,如果你希望從頭開始重建整個程序嗅回,就非常方便及穗。
? ? ? 現(xiàn)在你可能會想,“好吧绵载,這似乎沒問題埂陆,但這些makefile確實很麻煩!”你說得對——如果它們總是這樣寫的話娃豹。幸運的是焚虱,有很多快捷方式,讓使用更容易懂版。例如鹃栽,這個makefile具有相同的功能,但用起來更好:
? # specify all source files here
? SRCS = hw.c helper.c
? # specify target here (name of executable)
? TARG = hw
? # specify compiler, compile flags, and needed libs
? CC = gcc
? OPTS = -Wall -O
? LIBS = -lm
? # this translates .c files in src list to .o’s
? OBJS = $(SRCS:.c=.o)
? # all is not really needed, but is used to generate the target
? all: $(TARG)
? # this generates the target executable
? $(TARG): $(OBJS)
? $(CC) -o $(TARG) $(OBJS) $(LIBS)
? # this is a generic rule for .o files
? %.o: %.c
? $(CC) $(OPTS) -c $< -o $@
? # and finally, a clean line
? clean:
? rm -f $(OBJS) $(TARG)
? ? ? 雖然我們不會詳細介紹make語法躯畴,但如你所見民鼓,這個makefile可以讓生活更輕松一些薇芝。例如,它允許你輕松地將新的源文件添加到構(gòu)建中丰嘉,只需將它們加入makefile頂部的SRCS變量即可夯到。你還可以通過更改TARG行輕松更改可執(zhí)行文件的名稱,并且可以輕松修改指定編譯器供嚎,標志和庫黄娘。
? ? ? 關(guān)于make的最后一句話:找出目標的先決條件并非總是很容易,特別是在大型復(fù)雜程序中克滴。毫不奇怪逼争,有另一種工具可以幫助解決這個問題,稱為makedepend劝赔。自己閱讀它誓焦,看看是否可以將它合并到一個makefile中。
? ? ? E.7 調(diào)試
? ? ? 最后着帽,在創(chuàng)建了良好的構(gòu)建環(huán)境和正確編譯的程序之后杂伟,你可能會發(fā)現(xiàn)程序有問題。解決問題的一種方法是認真思考——這種方法有時會成功仍翰,但往往不會赫粥。問題是缺乏信息。你只是不知道程序中到底發(fā)生了什么予借,因此無法弄清楚為什么它沒有按預(yù)期運行越平。幸運的是,有某種幫助工具:gdb灵迫,GNU調(diào)試器秦叛。
? ? ? 將以下錯誤代碼保存在buggy.c文件中,然后編譯成可執(zhí)行文件瀑粥。
? #include <stdio.h>
? struct Data {
? int x;
? };
? int
? main(int argc, char *argv[])
? {
? struct Data *p = NULL;
? printf("%d\n", p->x);
? }
? ? ? 在這個例子中挣跋,主程序在變量p為NULL時引用它,這將導(dǎo)致分段錯誤狞换。當(dāng)然避咆,這個問題應(yīng)該很容易通過檢查來解決,但在更復(fù)雜的程序中哀澈,找到這樣的問題并非總是那么容易牌借。
? ? ? 要為調(diào)試會話做好準備,請重新編譯程序割按,并確保將-g標志加入每個編譯行膨报。這讓可執(zhí)行文件包含額外的調(diào)試信息,這些信息在調(diào)試會話期間非常有用。另外现柠,不要打開優(yōu)化(-O)院领。盡管這可能也行,但在調(diào)試過程中也可能導(dǎo)致困擾够吩。
? ? ? 使用-g重新編譯后比然,你就可以使用調(diào)試器了。在命令提示符處啟動gdb周循,如下所示:
? prompt> gdb buggy
? ? ? 這讓你進入與調(diào)試器的交互式會話强法。請注意,你還可以使用調(diào)試器來檢查在錯誤運行期間生成的“核心”文件湾笛,或者連上已在運行的程序饮怯。閱讀文檔以了解更多相關(guān)信息。
? ? ? 進入調(diào)試器后嚎研,你可能會看到以下內(nèi)容:
? prompt> gdb buggy
? GNU gdb ...
? Copyright 2008 Free Software Foundation, Inc.
? (gdb)
? ? ? 你可能想要做的第一件事就是繼續(xù)運行程序蓖墅。這只需在gdb命令提示符下輸入run。在這個例子中临扮,你可能會看到:
? (gdb) run
? Starting program: buggy
? Program received signal SIGSEGV, Segmentation fault.
? 0x8048433 in main (argc=1, argv=0xbffff844) at buggy.cc:19
? 19 printf("%d\n", p->x);
? ? ? 從示例中可以看出论矾,在這種情況下,gdb會立即指出問題發(fā)生的位置杆勇。在我們嘗試引用p的行中產(chǎn)生了“分段錯誤”贪壳。這就意味著我們訪問了一些我們不應(yīng)該訪問的內(nèi)存。這時蚜退,精明的程序員可以檢查代碼寥袭,然后說“啊哈!肯定是p沒有指向任何有效的地址关霸,因此不應(yīng)該引用!”杰扫,然后繼續(xù)修復(fù)該問題队寇。
? ? ? 但是,如果你不知道發(fā)生了什么章姓,可能想要檢查一些變量佳遣。gdb允許你在調(diào)試會話期間以交互方式執(zhí)行此操作。
? (gdb) print p
? 1 = (Data *) 0x0
? ? ? 通過使用print原語凡伊,我們可以檢查p零渐,并看到它是指向Data類型結(jié)構(gòu)的指針,并且它當(dāng)前設(shè)置為NULL(即零系忙,即十六進制零诵盼,此處顯示為“0x0”)。
? ? ? 最后,你還可以在程序中設(shè)置斷點风宁,讓調(diào)試器在某個函數(shù)中停止程序洁墙。執(zhí)行此操作后,單步執(zhí)行(一次一行)戒财,看看發(fā)生了什么热监,這通常很有用。
? (gdb) break main
? Breakpoint 1 at 0x8048426: file buggy.cc, line 17.
? (gdb) run
? Starting program: /homes/hacker/buggy
? Breakpoint 1, main (argc=1, argv=0xbffff844) at buggy.cc:17
? 17 struct Data *p = NULL;
? (gdb) next
? 19 printf("%d\n", p->x);
? (gdb)
? Program received signal SIGSEGV, Segmentation fault.
? 0x8048433 in main (argc=1, argv=0xbffff844) at buggy.cc:19
? 19 printf("%d\n", p->x);
? ? ? 在上面的例子中饮寞,在main()函數(shù)中設(shè)置了斷點孝扛。因此,當(dāng)我們運行程序時幽崩,調(diào)試器幾乎立即停止在main執(zhí)行苦始。在示例中的該點處,發(fā)出“next”命令歉铝,它將執(zhí)行下一行源代碼級指令盈简。“next”和“step”都是繼續(xù)執(zhí)行程序的有用方法——在文檔中閱讀太示,以獲取更多詳細信息[2]柠贤。
? ? ? 這里的討論真的對gdb不公平,它是豐富而靈活的調(diào)試工具类缤,有許多功能臼勉,而不只是這里有限篇幅中描述的功能。在閑暇之余閱讀更多相關(guān)信息餐弱,你將成為一名專家宴霸。
? ? ? E.8 文檔
? ? ? 要了解有關(guān)所有這些事情的更多信息,你必須做兩件事:第一是使用這些工具膏蚓;第二是自己閱讀更多相關(guān)信息瓢谢。了解更多關(guān)于gcc、gmake和gdb的一種方法是閱讀它們的手冊頁驮瞧。在命令提示符下輸入man gcc氓扛、man gmake或man gdb。你還可以使用man -k在手冊頁中搜索關(guān)鍵字论笔,但這并非總?cè)缛艘獠衫伞9雀杷阉骺赡苁歉玫姆椒ā?/p>
? ? ? 關(guān)于手冊頁有一個棘手的事情:如果有多個名為×××的東西,輸入man ×××可能不會得到你想要的東西狂魔。例如蒜埋,如果你正在尋找kill()系統(tǒng)調(diào)用手冊頁,如果只是在提示符下鍵入man kill最楷,會得到錯誤的手冊頁整份,因為有一個名為kill的命令行程序待错。手冊頁分為幾個部分(section),默認情況下皂林,man將返回找到的最低層部分的手冊頁朗鸠,在本例中為第1部分。請注意础倍,你可以通過查看頁面的頂部來確定你看到的手冊頁:如果看到kill(2)烛占,就知道你在第2節(jié)的正確手冊頁中,這里放的是系統(tǒng)調(diào)用沟启。有關(guān)手冊頁的每個不同部分中存儲的內(nèi)容忆家,請鍵入man man以了解更多信息。另外請注意德迹,man -a kill可用于遍歷名為“kill”的所有手冊頁芽卿。
? ? ? 手冊頁對于查找許多內(nèi)容非常有用。特別是胳搞,你經(jīng)常需要查找要傳遞給庫調(diào)用的參數(shù)卸例,或者需要包含哪些頭文件才能使用庫調(diào)用。所有這些都在手冊頁中提供肌毅。例如筷转,如果查找open()系統(tǒng)調(diào)用,你會看到:
? SYNOPSIS
? #include <sys/types.h>
? #include <sys/stat.h>
? #include <fcntl.h>
? int open(const char *path, int oflag, /* mode_t mode */...);
? ? ? 這告訴你包含頭文件sys/types.h悬而、sys/stat.h和fcntl.h呜舒,以便使用open調(diào)用俗扇。它還告訴你要傳遞給open的參數(shù)镣屹,即名為path的字符串和整數(shù)標志oflag唠雕,以及指定文件模式的可選參數(shù)衷旅。如果你需要鏈接某個庫以使用該調(diào)用,這里也會告訴你涉枫。
? ? ? 需要一些努力才能有效使用手冊頁梧田。它們通常分為許多標準部分萌业。主體將描述如何傳遞不同的參數(shù)蔚袍,以使函數(shù)具有不同的行為左电。
? ? ? 一個特別有用的部分是手冊頁的RETURN VALUES部分,它告訴你成功或失敗時函數(shù)將返回什么页响。再次引用open()的手冊頁:
? RETURN VALUES
? Upon successful completion, the open() function opens the
? file and return a non-negative integer representing the
? lowest numbered unused file descriptor. Otherwise, -1 is
? returned, errno is set to indicate the error, and no files
? are created or modified.
? ? ? 因此,通過檢查open的返回值段誊,你可以看到是否成功打開闰蚕。如果沒有,open(以及許多標準庫函數(shù))會將一個名為errno的全局變量設(shè)置為一個值连舍,來告訴你錯誤没陡。有關(guān)更多詳細信息,請參見手冊頁的ERRORS部分。
? ? ? 你可能還想做一件事盼玄,即查找未在手冊頁本身中指定的結(jié)構(gòu)的定義贴彼。例如,gettimeofday()的手冊頁有以下概要:
? SYNOPSIS
? #include <sys/time.h>
? int gettimeofday(struct timeval *restrict tp,
? void *restrict tzp);
? ? ? 在這個頁面中埃儿,你可以看到時間被放入timeval類型的結(jié)構(gòu)中器仗,但是手冊頁可能不會告訴你這個結(jié)構(gòu)有哪些字段!(在這個例子中童番,它包含在內(nèi)精钮,但你可能并非總是如此幸運)因此,你可能不得不尋找它剃斧。所有包含文件都位于/usr/include目錄下轨香,因此你可以用grep這樣的工具來查找它。例如幼东,你可以鍵入:
? prompt> grep ’struct timeval’ /usr/include/sys/*.h
? ? ? 這讓你在/usr/include/sys中以.h結(jié)尾的所有文件中查找該結(jié)構(gòu)的定義臂容。遺憾的是,這可能不一定有效根蟹,因為包含文件可能包括在別處的其他文件小編是一個有著5年開發(fā)經(jīng)驗的C/C++程序員脓杉,關(guān)于C/C++,自己有做材料的整合娜亿,一個完整的學(xué)習(xí)C/C++的路線丽已,學(xué)習(xí)材料和工具給大家!企鵝<C語言C++編程學(xué)習(xí)>买决!希望你也能憑自己的努力沛婴,成為下一個優(yōu)秀的程序員。
? ? ? 更好的方法是使用你可以使用的工具督赤,即編譯器嘁灯。編寫一個包含頭文件time.h的程序,假設(shè)名為main.c躲舌。然后丑婿,使用編譯器調(diào)用預(yù)處理器,而不是編譯它没卸。預(yù)處理器處理文件中的所有指令羹奉,例如#define指令和#include指令。為此约计,請鍵入gcc -E main.c诀拭。結(jié)果是一個C文件,其中包含所有需要的結(jié)構(gòu)和原型煤蚌,包括timeval結(jié)構(gòu)的定義耕挨。
? ? ? 可能還有找到這些東西的更好方法:google细卧。你應(yīng)該總是google那些你不了解的東西——只要通過查找就可以學(xué)到很多東西,這令人驚奇筒占!
? ? ? info頁面
? ? ? info頁面在尋找文檔方面也非常有用贪庙,它為許多GNU工具提供了更詳細的文檔。你可以通過運行info程序或通過emacs(黑客的首選編輯器)執(zhí)行Meta-x info來訪問info頁面翰苫。像gcc這樣的程序有數(shù)百個標志止邮,其中一些標志非常有用。gmake還有許多功能可以改善你的構(gòu)建環(huán)境革骨。最后农尖,gdb是一個非常復(fù)雜的調(diào)試器。閱讀man和info頁面良哲,嘗試以前沒有嘗試過的功能盛卡,成為編程工具的強大用!
全球最大的C/C++筑凫、編程愛好者的聚集地就在我這里滑沧,<C語言C++編程學(xué)習(xí)>!歡迎初學(xué)和進階中的小伙伴巍实。希望你也能憑自己的努力滓技,成為下一個優(yōu)秀的程序員。工作需要棚潦、感興趣令漂、為了入行、轉(zhuǎn)行需要學(xué)習(xí)C/C++的伙伴可以跟我一起學(xué)習(xí)丸边!”
關(guān)注我和我的專欄叠必,帶你遨游代碼世界!C語言/C++進階之路 - 專題 - 簡書