GCC詳解及makefile規則_第1頁
GCC詳解及makefile規則_第2頁
GCC詳解及makefile規則_第3頁
GCC詳解及makefile規則_第4頁
GCC詳解及makefile規則_第5頁
已閱讀5頁,還剩30頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、GCC 編譯詳解GNU CC(簡稱為Gcc)是GNU項目中符合ANSI C標準的編譯系統,能夠編譯用C、C+和Object C等語言編寫的程序。Gcc不僅功能強大,而且可以編譯如C、C+、Object C、Java、Fortran、Pascal、Modula-3和Ada等多種語言,而且Gcc又是一個交叉平臺編譯器,它能夠在當前CPU平臺上為多種不同體系結構的硬件平臺開發軟件,因此尤其適合在嵌入式領域的開發編譯。本章中的示例,除非特別注明,否則均采用Gcc版本為4.0.0。GCC入門基礎表3.6 Gcc所支持后綴名解釋后 綴 名所對應的語言后 綴 名所對應的語言.cC原始程序.s/.S匯編語言原

2、始程序.C/.cc/.cxxC+原始程序.h預處理文件(頭文件).mObjective-C原始程序.o目標文件.i已經過預處理的C原始程序.a/.so編譯后的庫文件.ii已經過預處理的C+原始程序如本章開頭提到的,Gcc的編譯流程分為了四個步驟,分別為:· 預處理(Pre-Processing)· 編譯(Compiling)· 匯編(Assembling)· 鏈接(Linking)下面就具體來查看一下Gcc是如何完成四個步驟的。首先,有以下hello.c源代碼#include<stdio.h>int main()printf("He

3、llo! This is our embedded world!n");return 0;(1)預處理階段在該階段,編譯器將上述代碼中的stdio.h編譯進來,并且用戶可以使用Gcc的選項”-E”進行查看,該選項的作用是讓Gcc在預處理結束后停止編譯過程。注意Gcc指令的一般格式為:Gcc 選項 要編譯的文件 選項 目標文件其中,目標文件可缺省,Gcc默認生成可執行的文件,命為:編譯文件.outrootlocalhost Gcc# Gcc E hello.c o hello.i在此處,選項”-o”是指目標文件,由表3.6可知,”.i”文件為已經過預處理的C原始程序。以下列出了hell

4、o.i文件的部分內容:typedef int (*_gconv_trans_fct) (struct _gconv_step *,struct _gconv_step_data *, void *,_const unsigned char *,_const unsigned char *,_const unsigned char *, unsigned char *,size_t *);# 2 "hello.c" 2int main()printf("Hello! This is our embedded world!n");return 0;由此可見,

5、Gcc確實進行了預處理,它把”stdio.h”的內容插入到hello.i文件中。(2)編譯階段接下來進行的是編譯階段,在這個階段中,Gcc首先要檢查代碼的規范性、是否有語法錯誤等,以確定代碼的實際要做的工作,在檢查無誤后,Gcc把代碼翻譯成匯編語言。用戶可以使用”-S”選項來進行查看,該選項只進行編譯而不進行匯編,生成匯編代碼。rootlocalhost Gcc# Gcc S hello.i o hello.s以下列出了hello.s的內容,可見Gcc已經將其轉化為匯編了,感興趣的讀者可以分析一下這一行簡單的C語言小程序是如何用匯編代碼實現的。.file "hello.c"

6、.section .rodata.align 4.LC0:.string"Hello! This is our embedded world!".text.globl main.type main, functionmain:pushl pmovl %esp, psubl $8, %espandl $-16, %espmovl $0, xaddl $15, xaddl $15, xshrl $4, xsall $4, xsubl x, %espsubl $12, %esppushl $.LC0call putsaddl $16, %espmovl $0, xleaveret

7、.size main, .-main.ident "GCC: (GNU) 4.0.0 20050519 (Red Hat 4.0.0-8)".section .note.GNU-stack,"",progbits(3)匯編階段匯編階段是把編譯階段生成的”.s”文件轉成目標文件,讀者在此可使用選項”-c”就可看到匯編代碼已轉化為”.o”的二進制目標代碼了。如下所示:rootlocalhost Gcc# Gcc c hello.s o hello.o(4)鏈接階段在成功編譯之后,就進入了鏈接階段。在這里涉及到一個重要的概念:函數庫。讀者可以重新查看這個小程序

8、,在這個程序中并沒有定義”printf”的函數實現,且在預編譯中包含進的”stdio.h”中也只有該函數的聲明,而沒有定義函數的實現,那么,是在哪里實現”printf”函數的呢?最后的答案是:系統把這些函數實現都被做到名為libc.so.6的庫文件中去了,在沒有特別指定時,Gcc會到系統默認的搜索路徑”/usr/lib”下進行查找,也就是鏈接到libc.so.6庫函數中去,這樣就能實現函數”printf”了,而這也就是鏈接的作用。函數庫一般分為靜態庫和動態庫兩種。靜態庫是指編譯鏈接時,把庫文件的代碼全部加入到可執行文件中,因此生成的文件比較大,但在運行時也就不再需要庫文件了。其后綴名一般為”.

9、a”。動態庫與之相反,在編譯鏈接時并沒有把庫文件的代碼加入到可執行文件中,而是在程序執行時由運行時鏈接文件加載庫,這樣可以節省系統的開銷。動態庫一般后綴名為”.so”,如前面所述的libc.so.6就是動態庫。Gcc在編譯時默認使用動態庫。完成了鏈接之后,Gcc就可以生成可執行文件,如下所示。rootlocalhost Gcc# Gcc hello.o o hello運行該可執行文件,出現正確的結果如下。rootlocalhost Gcc# ./helloHello! This is our embedded world!Gcc編譯選項分析Gcc有超過100個的可用選項,主要包括總體選項、告警

10、和出錯選項、優化選項和體系結構相關選項。以下對每一類中最常用的選項進行講解。(1)總體選項Gcc的總結選項如表3.7所示,很多在前面的示例中已經有所涉及。表3.7 Gcc總體選項列表后綴名所對應的語言-c只是編譯不鏈接,生成目標文件“.o”-S只是編譯不匯編,生成匯編代碼-E只進行預編譯,不做其他處理-g在可執行程序中包含標準調試信息-o file把輸出文件輸出到file里-v打印出編譯器內部編譯各過程的命令行信息和編譯器的版本-I dir在頭文件的搜索路徑列表中添加dir目錄-L dir在庫文件的搜索路徑列表中添加dir目錄-static鏈接靜態庫-llibrary連接名為library的庫

11、文件對于“-c”、“-E”、“-o”、“-S”選項在前一小節中已經講解了其使用方法,在此主要講解另外兩個非常常用的庫依賴選項“-I dir”和“-L dir”。· “-I dir”正如上表中所述,“-I dir”選項可以在頭文件的搜索路徑列表中添加dir目錄。由于Linux中頭文件都默認放到了“/usr/include/”目錄下,因此,當用戶希望添加放置在其他位置的頭文件時,就可以通過“-I dir”選項來指定,這樣,Gcc就會到相應的位置查找對應的目錄。比如在“/root/workplace/Gcc”下有兩個文件:#include<my.h>int main()prin

12、tf(“Hello!n”);return 0;#include<stdio.h>這樣,就可在Gcc命令行中加入“-I”選項:rootlocalhost Gcc Gcc hello1.c I /root/workplace/Gcc/ -o hello1這樣,Gcc就能夠執行出正確結果。小知識在include語句中,“<>”表示在標準路徑中搜索頭文件,“”表示在本目錄中搜索。故在上例中,可把hello1.c的“#include<my.h>”改為“#include “my.h”,就不需要加上“-I”選項了。· “-L dir”選項“-L dir”的功能與

13、“-I dir”類似,能夠在庫文件的搜索路徑列表中添加dir目錄。例如有程序hello_sq.c需要用到目錄“/root/workplace/Gcc/lib”下的一個動態庫libsunq.so,則只需鍵入如下命令即可:rootlocalhost Gcc Gcc hello_sq.c L /root/workplace/Gcc/lib lsunq o hello_sq需要注意的是,“-I dir”和“-L dir”都只是指定了路徑,而沒有指定文件,因此不能在路徑中包含文件名。另外值得詳細解釋一下的是“-l”選項,它指示Gcc去連接庫文件libsunq.so。由于在Linux下的庫文件命名時有一個

14、規定:必須以lib三個字母開頭。因此在用-l選項指定鏈接的庫文件名時可以省去lib三個字母。也就是說Gcc在對”-lsunq”進行處理時,會自動去鏈接名為libsunq.so的文件。(2)告警和出錯選項Gcc的告警和出錯選項如表3.8所示。表3.8 Gcc總體選項列表選項含義-ansi支持符合ANSI標準的C程序-pedantic允許發出ANSI C標準所列的全部警告信息選項含義-pedantic-error允許發出ANSI C標準所列的全部錯誤信息-w關閉所有告警-Wall允許發出Gcc提供的所有有用的報警信息-werror把所有的告警信息轉化為錯誤信息,并在告警發生時終止編譯過程下面結合實

15、例對這幾個告警和出錯選項進行簡單的講解。如有以下程序段:#include<stdio.h>void main()long long tmp = 1;printf(“This is a bad code!n”);return 0;這是一個很糟糕的程序,讀者可以考慮一下有哪些問題?· “-ansi”該選項強制Gcc生成標準語法所要求的告警信息,盡管這還并不能保證所有沒有警告的程序都是符合ANSI C標準的。運行結果如下所示:rootlocalhost Gcc# Gcc ansi warning.c o warningwarning.c: 在函數“main”中:warning.

16、c:7 警告:在無返回值的函數中,“return”帶返回值warning.c:4 警告:“main”的返回類型不是“int”可以看出,該選項并沒有發現”long long”這個無效數據類型的錯誤。· “-pedantic”允許發出ANSI C標準所列的全部警告信息,同樣也保證所有沒有警告的程序都是符合ANSI C標準的。其運行結果如下所示:rootlocalhost Gcc# Gcc pedantic warning.c o warningwarning.c: 在函數“main”中:warning.c:5 警告:ISO C90不支持“long long”warning.c:7 警告:

17、在無返回值的函數中,“return”帶返回值warning.c:4 警告:“main”的返回類型不是“int”可以看出,使用該選項查看出了”long long”這個無效數據類型的錯誤。· “-Wall”允許發出Gcc能夠提供的所有有用的報警信息。該選項的運行結果如下所示:rootlocalhost Gcc# Gcc Wall warning.c o warningwarning.c:4 警告:“main”的返回類型不是“int”warning.c: 在函數”main”中:warning.c:7 警告:在無返回值的函數中,”return”帶返回值warning.c:5 警告:未使用的變

18、量“tmp”使用“-Wall”選項找出了未使用的變量tmp,但它并沒有找出無效數據類型的錯誤。另外,Gcc還可以利用選項對單獨的常見錯誤分別指定警告,有關具體選項的含義感興趣的讀者可以查看Gcc手冊進行學習。(3)優化選項Gcc可以對代碼進行優化,它通過編譯選項“-On”來控制優化代碼的生成,其中n是一個代表優化級別的整數。對于不同版本的Gcc來講,n的取值范圍及其對應的優化效果可能并不完全相同,比較典型的范圍是從0變化到2或3。不同的優化級別對應不同的優化處理工作。如使用優化選項“-O”主要進行線程跳轉(Thread Jump)和延遲退棧(Deferred Stack Pops)兩種優化。使

19、用優化選項“-O2”除了完成所有“-O1”級別的優化之外,同時還要進行一些額外的調整工作,如處理器指令調度等。選項“-O3”則還包括循環展開和其他一些與處理器特性相關的優化工作。雖然優化選項可以加速代碼的運行速度,但對于調試而言將是一個很大的挑戰。因為代碼在經過優化之后,原先在源程序中聲明和使用的變量很可能不再使用,控制流也可能會突然跳轉到意外的地方,循環語句也有可能因為循環展開而變得到處都有,所有這些對調試來講都將是一場噩夢。所以筆者建議在調試的時候最好不使用任何優化選項,只有當程序在最終發行的時候才考慮對其進行優化。(4)體系結構相關選項Gcc的體系結構相關選項如表3.9所示。表3.9Gc

20、c體系結構相關選項列表選項含義-mcpu=type針對不同的CPU使用相應的CPU指令。可選擇的type有i386、i486、pentium及i686等-mieee-fp使用IEEE標準進行浮點數的比較-mno-ieee-fp不使用IEEE標準進行浮點數的比較-msoft-float輸出包含浮點庫調用的目標代碼-mshort把int類型作為16位處理,相當于short int-mrtd強行將函數參數個數固定的函數用ret NUM返回,節省調用函數的一條指令這些體系結構相關選項在嵌入式的設計中會有較多的應用,讀者需根據不同體系結構將對應的選項進行組合處理。在本書后面涉及到具體實例會有針對性的講解

21、。Gdb調試器調試是所有程序員都會面臨的問題。如何提高程序員的調試效率,更好更快地定位程序中的問題從而加快程序開發的進度,是大家共同面對的。就如讀者熟知的Windows下的一些調試工具,如VC自帶的如設置斷點、單步跟蹤等,都受到了廣大用戶的贊賞。那么,在Linux下有什么很好的調試工具呢?本文所介紹的Gdb調試器是一款GNU開發組織并發布的UNIX/Linux下的程序調試工具。雖然,它沒有圖形化的友好界面,但是它強大的功能也足以與微軟的VC工具等媲美。下面就請跟隨筆者一步步學習Gdb調試器。Gdb使用流程首先,筆者給出了一個短小的程序,由此帶領讀者熟悉一下Gdb的使用流程。強烈建議讀者能夠實際

22、動手操作。首先,打開Linux下的編輯器Vi或者Emacs,編輯如下代碼。(由于為了更好地熟悉Gdb的操作,筆者在此使用Vi編輯,希望讀者能夠參見3.3節中對Vi的介紹,并熟練使用Vi)。#include <stdio.h>int sum(int m);int main()int i,n=0;sum(50);for(i=1; i<=50; i+)n += i;printf("The sum of 1-50 is %d n", n );int sum(int m)int i,n=0;for(i=1; i<=m;i+)n += i;printf(&quo

23、t;The sum of 1-m is %dn", n);在保存退出后首先使用Gcc對test.c進行編譯,注意一定要加上選項”-g”,這樣編譯出的可執行代碼中才包含調試信息,否則之后Gdb無法載入該可執行文件。rootlocalhost Gdb# gcc -g test.c -o test雖然這段程序沒有錯誤,但調試完全正確的程序可以更加了解Gdb的使用流程。接下來就啟動Gdb進行調試。注意,Gdb進行調試的是可執行文件,而不是如”.c”的源代碼,因此,需要先通過Gcc編譯生成可執行文件才能用Gdb進行調試。rootlocalhost Gdb# gdb testGNU Gdb Re

24、d Hat Linux (-1.21rh)Copyright 2004 Free Software Foundation, Inc.GDB is free software, covered by the GNU General Public License, and you arewelcome to change it and/or distribute copies of it under certain conditions.Type "show copying" to see the conditions.There is absolutely no

25、 warranty for GDB. Type "show warranty" for details.This GDB was configured as "i386-redhat-linux-gnu".Using host libthread_db library "/lib/libthread_db.so.1".(gdb)可以看出,在Gdb的啟動畫面中指出了Gdb的版本號、使用的庫文件等信息,接下來就進入了由“(gdb)”開頭的命令行界面了。(1)查看文件在Gdb中鍵入”l”(list)就可以查看所載入的文件,如下所示:注意在G

26、db的命令中都可使用縮略形式的命令,如“l”代便“list”,“b”代表“breakpoint”,“p”代表“print”等,讀者也可使用“help”命令查看幫助信息。(Gdb) l1 #include <stdio.h>2 int sum(int m);3 int main()4 5 int i,n=0;6 sum(50);7 for(i=1; i<=50; i+)8 9 n += i;10 (Gdb) l11 printf("The sum of 150 is %d n", n );1213 14 int sum(int m)15 16 int i,n

27、=0;17 for(i=1; i<=m;i+)18 n += i;19 printf("The sum of 1m is = %dn", n);20 可以看出,Gdb列出的源代碼中明確地給出了對應的行號,這樣就可以大大地方便代碼的定位。(2)設置斷點設置斷點是調試程序中是一個非常重要的手段,它可以使程序到一定位置暫停它的運行。因此,程序員在該位置處可以方便地查看變量的值、堆棧情況等,從而找出代碼的癥結所在。在Gdb中設置斷點非常簡單,只需在”b”后加入對應的行號即可(這是最常用的方式,另外還有其他方式設置斷點)。如下所示:(Gdb) b 6Breakpoint 1 a

28、t 0x804846d: file test.c, line 6.要注意的是,在Gdb中利用行號設置斷點是指代碼運行到對應行之前將其停止,如上例中,代碼運行到第五行之前暫停(并沒有運行第五行)。(3)查看斷點情況在設置完斷點之后,用戶可以鍵入”info b”來查看設置斷點情況,在Gdb中可以設置多個斷點。(Gdb) info bNum Type Disp Enb Address What1 breakpoint keep y 0x0804846d in main at test.c:6(4)運行代碼接下來就可運行代碼了,Gdb默認從首行開始運行代碼,可鍵入”r”(run)即可(若想從程序中指定

29、行開始運行,可在r后面加上行號)。(Gdb) rStarting program: /root/workplace/Gdb/testReading symbols from shared object read from target memory.done.Loaded system supplied DSO at 0x5fb000Breakpoint 1, main () at test.c:66 sum(50);可以看到,程序運行到斷點處就停止了。(5)查看變量值在程序停止運行之后,程序員所要做的工作是查看斷點處的相關變量值。在Gdb中只需鍵入”p”變量值即可,如下所示:(Gdb) p

30、n$1 = 0(Gdb) p i$2 = 134518440在此處,為什么變量”i”的值為如此奇怪的一個數字呢?原因就在于程序是在斷點設置的對應行之前停止的,那么在此時,并沒有把”i”的數值賦為零,而只是一個隨機的數字。但變量”n”是在第四行賦值的,故在此時已經為零。小技巧Gdb在顯示變量值時都會在對應值之前加上”$N”標記,它是當前變量值的引用標記,所以以后若想再次引用此變量就可以直接寫作”$N”,而無需寫冗長的變量名。(6)單步運行單步運行可以使用命令”n”(next)或”s”(step),它們之間的區別在于:若有函數調用的時候,”s”會進入該函數而”n”不會進入該函數。因此,”s”就類似

31、于VC等工具中的”step in”,”n”類似與VC等工具中的”step over”。它們的使用如下所示:(Gdb) nThe sum of 1-m is 12757 for(i=1; i<=50; i+)(Gdb) ssum (m=50) at test.c:1616 int i,n=0;可見,使用”n”后,程序顯示函數sum的運行結果并向下執行,而使用”s”后則進入到sum函數之中單步運行。(7)恢復程序運行在查看完所需變量及堆棧情況后,就可以使用命令”c”(continue)恢復程序的正常運行了。這時,它會把剩余還未執行的程序執行完,并顯示剩余程序中的執行結果。以下是之前使用”n”

32、命令恢復后的執行結果:(Gdb) cContinuing.The sum of 1-50 is :1275Program exited with code 031.可以看出,程序在運行完后退出,之后程序處于“停止狀態”。小知識在Gdb中,程序的運行狀態有“運行”、“暫停”和“停止”三種,其中“暫停”狀態為程序遇到了斷點或觀察點之類的,程序暫時停止運行,而此時函數的地址、函數參數、函數內的局部變量都會被壓入“棧”(Stack)中。故在這種狀態下可以查看函數的變量值等各種屬性。但在函數處于“停止”狀態之后,“棧”就會自動撤銷,它也就無法查看各種信息了。Gdb基本命令Gdb的命令可以通過查看help

33、進行查找,由于Gdb的命令很多,因此Gdb的help將其分成了很多種類(class),用戶可以通過進一步查看相關class找到相應命令。如下所示:(gdb) helpList of classes of commands:aliases - Aliases of other commandsbreakpoints - Making program stop at certain pointsdata - Examining datafiles - Specifying and examining filesinternals - Maintenance commandsType "h

34、elp" followed by a class name for a list of commands in that class.Type "help" followed by command name for full documentation.Command name abbreViations are allowed if unambiguous.上述列出了Gdb各個分類的命令,注意底部的加粗部分說明其為分類命令。接下來可以具體查找各分類種的命令。如下所示:(gdb) help dataExamining data.List of commands:c

35、all - Call a function in the programdelete display - Cancel some expressions to be displayed when program stopsdelete mem - Delete memory regiondisable display - Disable some expressions to be displayed when program stopsType "help" followed by command name for full documentation.Command n

36、ame abbreViations are allowed if unambiguous.至此,若用戶想要查找call命令,就可鍵入“help call”。(gdb) help callCall a function in the program.The argument is the function name and arguments, in the notation of thecurrent working language. The result is printed and saved in the valuehistory, if it is not void.當然,若用戶已知

37、命令名,直接鍵入“help command”也是可以的。Gdb中的命令主要分為以下幾類:工作環境相關命令、設置斷點與恢復命令、源代碼查看命令、查看運行數據相關命令及修改運行參數命令。以下就分別對這幾類的命令進行講解。1工作環境相關命令Gdb中不僅可以調試所運行的程序,而且還可以對程序相關的工作環境進行相應的設定,甚至還可以使用shell中的命令進行相關的操作,其功能極其強大。表3.10所示列出了Gdb常見工作環境相關命令。表3.10 Gdb工作環境相關命令命 令 格 式含義set args運行時的參數指定運行時參數,如:set args 2show args查看設置好的運行參數path dir

38、設定程序的運行路徑show paths查看程序的運行路徑set enVironment var =value設置環境變量show enVironment var查看環境變量cd dir進入到dir目錄,相當于shell中的cd命令pwd顯示當前工作目錄shell command運行shell的command命令2設置斷點與恢復命令Gdb中設置斷點與恢復的常見命令如表3.11所示。表3.11 Gdb設置斷點與恢復相關命令命 令 格 式含義bnfo b查看所設斷點break 行號或函數名 <條件表達式>設置斷點tbreak 行號或函數名 <條件表達式>設置臨時斷點,到達后被

39、自動刪除delete 斷點號刪除指定斷點,其斷點號為”info b”中的第一欄。若缺省斷點號則刪除所有斷點disable 斷點號停止指定斷點,使用”info b”仍能查看此斷點。同delete一樣,省斷點號則停止所有斷點enable 斷點號激活指定斷點,即激活被disable停止的斷點condition 斷點號 <條件表達式>修改對應斷點的條件ignore 斷點號<num>在程序執行中,忽略對應斷點num次step單步恢復程序運行,且進入函數調用next單步恢復程序運行,但不進入函數調用finish運行程序,直到當前函數完成返回c繼續執行函數,直到函數結束或遇到新的斷點

40、由于設置斷點在Gdb的調試中非常重要,所以在此再著重講解一下Gdb中設置斷點的方法。Gdb中設置斷點有多種方式:其一是按行設置斷點,設置方法在3.5.1節已經指出,在此就不重復了。另外還可以設置函數斷點和條件斷點,在此結合上一小節的代碼,具體介紹后兩種設置斷點的方法。 函數斷點Gdb中按函數設置斷點只需把函數名列在命令”b”之后,如下所示:(gdb) b sumBreakpoint 1 at 0x80484ba: file test.c, line 16.(gdb) info bNum Type Disp Enb Address What1 breakpoint keep y 0x080484

41、ba in sum at test.c:16要注意的是,此時的斷點實際是在函數的定義處,也就是在16行處(注意第16行還未執行)。 條件斷點Gdb中設置條件斷點的格式為:b 行數或函數名 if 表達式。具體實例如下所示:(gdb) b 8 if i=10Breakpoint 1 at 0x804848c: file test.c, line 8.(gdb) info bNum Type Disp Enb Address What1 breakpoint keep y 0x0804848c in main at test.c:8stop only if i = 10(gdb) rStarting

42、 program: /home/yul/testThe sum of 1-m is 1275Breakpoint 1, main () at test.c:99 n += i;(gdb) p i$1 = 10可以看到,該例中在第8行(也就是運行完第7行的for循環)設置了一個“i=0”的條件斷點,在程序運行之后可以看出,程序確實在i為10時暫停運行。3Gdb中源碼查看相關命令在Gdb中可以查看源碼以方便其他操作,它的常見相關命令如表3.12所示:表3.12 Gdb源碼查看相關相關命令命 令 格 式含義list <行號>|<函數名>查看指定位置代碼file 文件名加載指定

43、文件forward-search 正則表達式源代碼前向搜索reverse-search 正則表達式源代碼后向搜索dir dir停止路徑名show directories顯示定義了的源文件搜索路徑info line顯示加載到Gdb內存中的代碼4Gdb中查看運行數據相關命令Gdb中查看運行數據是指當程序處于“運行”或“暫停”狀態時,可以查看的變量及表達式的信息,其常見命令如表3.13所示:表3.13 Gdb查看運行數據相關命令命 令 格 式含義print 表達式|變量查看程序運行時對應表達式和變量的值x <n/f/u>查看內存變量內容。其中n為整數表示顯示內存的長度,f表示顯示的格式,

44、u表示從當前地址往后請求顯示的字節數display 表達式設定在單步運行或其他情況中,自動顯示的對應表達式的內容5Gdb中修改運行參數相關命令Gdb還可以修改運行時的參數,并使該變量按照用戶當前輸入的值繼續運行。它的設置方法為:在單步執行的過程中,鍵入命令“set 變量設定值”。這樣,在此之后,程序就會按照該設定的值運行了。下面,筆者結合上一節的代碼將n的初始值設為4,其代碼如下所示:(Gdb) b 7Breakpoint 5 at 0x804847a: file test.c, line 7.(Gdb) rStarting program: /home/yul/testThe sum of

45、1-m is 1275Breakpoint 5, main () at test.c:77 for(i=1; i<=50; i+)(Gdb) set n=4(Gdb) cContinuing.The sum of 1-50 is 1279Program exited with code 031.可以看到,最后的運行結果確實比之前的值大了4。Gdb的使用切記點:· 在Gcc編譯選項中一定要加入”-g”。· 只有在代碼處于“運行”或“暫停”狀態時才能查看變量值。· 設置斷點后程序在指定行之前停止。Make工程管理器到此為止,讀者已經了解了如何在Linux下使用編

46、輯器編寫代碼,如何使用Gcc把代碼編譯成可執行文件,還學習了如何使用Gdb來調試程序,那么,所有的工作看似已經完成了,為什么還需要Make這個工程管理器呢?所謂工程管理器,顧名思義,是指管理較多的文件的。讀者可以試想一下,有一個上百個文件的代碼構成的項目,如果其中只有一個或少數幾個文件進行了修改,按照之前所學的Gcc編譯工具,就不得不把這所有的文件重新編譯一遍,因為編譯器并不知道哪些文件是最近更新的,而只知道需要包含這些文件才能把源代碼編譯成可執行文件,于是,程序員就不能不再重新輸入數目如此龐大的文件名以完成最后的編譯工作。但是,請讀者仔細回想一下本書在3.1.2節中所闡述的編譯過程,編譯過程

47、是分為編譯、匯編、鏈接不同階段的,其中編譯階段僅檢查語法錯誤以及函數與變量的聲明是否正確聲明了,在鏈接階段則主要完成是函數鏈接和全局變量的鏈接。因此,那些沒有改動的源代碼根本不需要重新編譯,而只要把它們重新鏈接進去就可以了。所以,人們就希望有一個工程管理器能夠自動識別更新了的文件代碼,同時又不需要重復輸入冗長的命令行,這樣,Make工程管理器也就應運而生了。實際上,Make工程管理器也就是個“自動編譯管理器”,這里的“自動”是指它能夠根據文件時間戳自動發現更新過的文件而減少編譯的工作量,同時,它通過讀入Makefile文件的內容來執行大量的編譯工作。用戶只需編寫一次簡單的編譯語句就可以了。它大

48、大提高了實際項目的工作效率,而且幾乎所有Linux下的項目編程均會涉及到它,希望讀者能夠認真學習本節內容。Makefile基本結構Makefile是Make讀入的惟一配置文件,因此本節的內容實際就是講述Makefile的編寫規則。在一個Makefile中通常包含如下內容:· 需要由make工具創建的目標體(target),通常是目標文件或可執行文件;· 要創建的目標體所依賴的文件(dependency_file);· 創建每個目標體時需要運行的命令(command)。它的格式為:target: dependency_filescommand例如,有兩個文件分別為h

49、ello.c和hello.h,創建的目標體為hello.o,執行的命令為gcc編譯指令:gcc c hello.c,那么,對應的Makefile就可以寫為:#The simplest examplehello.o: hello.c hello.hgcc c hello.c o hello.o接著就可以使用make了。使用make的格式為:make target,這樣make就會自動讀入Makefile(也可以是首字母小寫makefile)并執行對應target的command語句,并會找到相應的依賴文件。如下所示:rootlocalhost makefile# make hello.ogcc

50、c hello.c o hello.orootlocalhost makefile# lshello.c hello.h hello.o Makefile可以看到,Makefile執行了“hello.o”對應的命令語句,并生成了“hello.o”目標體。注意在Makefile中的每一個command前必須有“Tab”符,否則在運行make命令時會出錯。Makefile變量上面示例的Makefile在實際中是幾乎不存在的,因為它過于簡單,僅包含兩個文件和一個命令,在這種情況下完全不必要編寫Makefile而只需在Shell中直接輸入即可,在實際中使用的Makefile往往是包含很多的文件和命令的

51、,這也是Makefile產生的原因。下面就可給出稍微復雜一些的Makefile進行講解:sunq:kang.o yul.oGcc kang.o bar.o -o myprogkang.o : kang.c kang.h head.hGcc Wall O -g c kang.c -o kang.oyul.o : bar.c head.hGcc - Wall O -g c yul.c -o yul.o在這個Makefile中有三個目標體(target),分別為sunq、kang.o和yul.o,其中第一個目標體的依賴文件就是后兩個目標體。如果用戶使用命令“make sunq”,則make管理器就是

52、找到sunq目標體開始執行。這時,make會自動檢查相關文件的時間戳。首先,在檢查“kang.o”、“yul.o”和“sunq”三個文件的時間戳之前,它會向下查找那些把“kang.o”或“yul.o”做為目標文件的時間戳。比如,“kang.o”的依賴文件為:“kang.c”、“kang.h”、“head.h”。如果這些文件中任何一個的時間戳比“kang.o”新,則命令“gcc Wall O -g c kang.c -o kang.o”將會執行,從而更新文件“kang.o”。在更新完“kang.o”或“yul.o”之后,make會檢查最初的“kang.o”、“yul.o”和“sunq”三個文件,

53、只要文件“kang.o”或“yul.o”中的任比文件時間戳比“sunq”新,則第二行命令就會被執行。這樣,make就完成了自動檢查時間戳的工作,開始執行編譯工作。這也就是Make工作的基本流程。接下來,為了進一步簡化編輯和維護Makefile,make允許在Makefile中創建和使用變量。變量是在Makefile中定義的名字,用來代替一個文本字符串,該文本字符串稱為該變量的值。在具體要求下,這些值可以代替目標體、依賴文件、命令以及makefile文件中其它部分。在Makefile中的變量定義有兩種方式:一種是遞歸展開方式,另一種是簡單方式。遞歸展開方式定義的變量是在引用在該變量時進行替換的,

54、即如果該變量包含了對其他變量的應用,則在引用該變量時一次性將內嵌的變量全部展開,雖然這種類型的變量能夠很好地完成用戶的指令,但是它也有嚴重的缺點,如不能在變量后追加內容(因為語句:CFLAGS = $(CFLAGS) -O在變量擴展過程中可能導致無窮循環)。為了避免上述問題,簡單擴展型變量的值在定義處展開,并且只展開一次,因此它不包含任何對其它變量的引用,從而消除變量的嵌套引用。遞歸展開方式的定義格式為:VAR=var簡單擴展方式的定義格式為:VAR:=varMake中的變量使用均使用格式為:$(VAR)注意變量名是不包括“:”、“#”、“=”結尾空格的任何字符串。同時,變量名中包含字母、數字

55、以及下劃線以外的情況應盡量避免,因為它們可能在將來被賦予特別的含義。變量名是大小寫敏感的,例如變量名“foo”、“FOO”、和“Foo”代表不同的變量。推薦在makefile內部使用小寫字母作為變量名,預留大寫字母作為控制隱含規則參數或用戶重載命令選項參數的變量名。下面給出了上例中用變量替換修改后的Makefile,這里用OBJS代替kang.o和yul.o,用CC代替Gcc,用CFLAGS代替“-Wall -O g”。這樣在以后修改時,就可以只修改變量定義,而不需要修改下面的定義實體,從而大大簡化了Makefile維護的工作量。經變量替換后的Makefile如下所示:OBJS = kang.

56、o yul.oCC = GccCFLAGS = -Wall -O -gsunq : $(OBJS)$(CC) $(OBJS) -o sunqkang.o : kang.c kang.h$(CC) $(CFLAGS) -c kang.c -o kang.oyul.o : yul.c yul.h$(CC) $(CFLAGS) -c yul.c -o yul.o可以看到,此處變量是以遞歸展開方式定義的。Makefile中的變量分為用戶自定義變量、預定義變量、自動變量及環境變量。如上例中的OBJS就是用戶自定義變量,自定義變量的值由用戶自行設定,而預定義變量和自動變量為通常在Makefile都會出現的變量,其中部分有默認值,也就是常見的設定值,當然用戶可以對其進行修改。預定義變量包含了常見編譯器、匯編器的名稱及其編譯選項。下表3.14列出了Makefile中常見預定義變量及其部分默認值。表3.14 Makefile中常見預定義變量命 令 格 式含義AR庫文件維護程序的名稱,默認值為arAS匯編程序的名稱,默認值為asCCC編譯器的名稱,默認值為ccCPPC預編譯

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論