UNIX系統(tǒng)上渗稍,程序員需要掌握的C編程環(huán)境的基礎(chǔ)知識佩迟!

?? ? ? ?這是一份非常簡短的文檔,可以幫助你熟悉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++進階之路 - 專題 - 簡書

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末妹窖,一起剝皮案震驚了整個濱河市纬朝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌骄呼,老刑警劉巖共苛,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蜓萄,居然都是意外死亡隅茎,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門嫉沽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來患膛,“玉大人,你說我怎么就攤上這事耻蛇∽俚牛” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵臣咖,是天一觀的道長跃捣。 經(jīng)常有香客問我,道長夺蛇,這世上最難降的妖魔是什么疚漆? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮刁赦,結(jié)果婚禮上娶聘,老公的妹妹穿的比我還像新娘。我一直安慰自己甚脉,他們只是感情好丸升,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著牺氨,像睡著了一般狡耻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上猴凹,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天夷狰,我揣著相機與錄音,去河邊找鬼郊霎。 笑死沼头,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的书劝。 我是一名探鬼主播进倍,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼庄撮!你這毒婦竟也來了背捌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤洞斯,失蹤者是張志新(化名)和其女友劉穎毡庆,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體烙如,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡么抗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了亚铁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蝇刀。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖徘溢,靈堂內(nèi)的尸體忽然破棺而出吞琐,到底是詐尸還是另有隱情捆探,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布站粟,位于F島的核電站黍图,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏奴烙。R本人自食惡果不足惜助被,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望切诀。 院中可真熱鬧揩环,春花似錦、人聲如沸幅虑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽翘单。三九已至吨枉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間哄芜,已是汗流浹背貌亭。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留认臊,地道東北人圃庭。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像失晴,于是被迫代替她去往敵國和親剧腻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容