GCC的编译和调试--入门介绍
生活随笔
收集整理的這篇文章主要介紹了
GCC的编译和调试--入门介绍
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
編譯與調(diào)試
1.1編譯的概念和理解
在進(jìn)行C程序開發(fā)時(shí),編譯就是將編寫的C語言代碼變成可執(zhí)行程序的過程,這一過程
是由編譯器來完成的。編譯器就是完成程序編譯工作的軟件,在進(jìn)行程序編譯時(shí)完成了一系
列復(fù)雜的過程。
1.1.1程序編譯的過程
在執(zhí)行這一操作時(shí),程序完成了復(fù)雜的過程。一個(gè)程序的編譯,需要完成詞法分析、語
法分析、中間代碼生成、代碼優(yōu)化、目標(biāo)代碼生成。本章將講解這些步驟的作用與原理。
(1)詞法分析。指的是對(duì)由字符組成的單詞進(jìn)行處理,從左至右逐個(gè)字符地對(duì)源程序進(jìn)
行掃描,產(chǎn)生一個(gè)個(gè)的單詞符號(hào)。然后把字符串的源程序改造成為單詞符號(hào)串的中間程序。
在編譯程序時(shí),這一過程是自動(dòng)完成的。編譯程序會(huì)對(duì)代碼的每一個(gè)單詞進(jìn)行檢查。如果單
詞發(fā)生錯(cuò)誤,編譯過程就會(huì)停止并顯示錯(cuò)誤。這時(shí)需要對(duì)程序中的錯(cuò)誤進(jìn)行修改。
(2)語法分析。語法分析器以單詞符號(hào)作為輸入,分析單詞符號(hào)串是否形成符合語法規(guī)
則的語句。例如,需要檢查表達(dá)式、賦值、循環(huán)等結(jié)構(gòu)是否完整和符合使用規(guī)則。在語法分
析時(shí),會(huì)分析出程序中錯(cuò)誤的語句,并顯示出結(jié)果。如果語法發(fā)生錯(cuò)誤,編譯任務(wù)是不能完
成的。
(3)中間代碼生成。中間代碼是源程序的一種內(nèi)部表示,或稱中間語言。程序進(jìn)行詞法
分析和語法分析以后,將程序轉(zhuǎn)換成中間代碼。這一轉(zhuǎn)換的作用是使程序的結(jié)構(gòu)更加簡(jiǎn)單和
規(guī)范。中間代碼生成操作是一個(gè)中間過程,與用戶是無關(guān)的。
(4)代碼優(yōu)化。代碼優(yōu)化是指對(duì)程序進(jìn)行多種等價(jià)變換,使得從變換后的程序能生成更
有效的目標(biāo)代碼。用戶可以在編譯程序時(shí)設(shè)置代碼優(yōu)化的參數(shù),可以針對(duì)不同的環(huán)境和設(shè)置
進(jìn)行優(yōu)化。
(5)目標(biāo)代碼生成。目標(biāo)代碼生成指的是產(chǎn)生可以執(zhí)行的應(yīng)用程序,這是編譯的最后一
個(gè)步驟。生成的程序是二進(jìn)制的機(jī)器語言,用戶只能運(yùn)行這個(gè)程序,而不能打開這個(gè)文件查
看程序的代碼。
1.1.2編譯器
所謂編譯器,是將編寫出的程序代碼轉(zhuǎn)換成計(jì)算機(jī)可以運(yùn)行的程序的軟件。在進(jìn)行C程
序開發(fā)時(shí),編寫出的代碼是源程序的代碼,是不能直接運(yùn)行的。需要用編譯器編譯成可以運(yùn)
行的二進(jìn)制程序。
在不同的操作系統(tǒng)下面有不同的編譯器。C程序是可以跨平臺(tái)運(yùn)行的。但并不是說
Windows系統(tǒng)下C語言編寫的程序可以直接在Linux下面運(yùn)行。Windows下面C語言編寫的
程序,被編譯成exe文件。這樣的程序只能在Windows系統(tǒng)下運(yùn)行。如果需要在Linux系統(tǒng)
下運(yùn)行,需要將這個(gè)程序的源代碼在Linux系統(tǒng)重新編譯。不同的操作系統(tǒng)下面有不同的編
譯器。Linux系統(tǒng)下面編譯生成的程序是不能在Windows系統(tǒng)上運(yùn)行的。
1.2 gcc編譯器
gcc是Linux下的C程序編譯器,具有非常強(qiáng)大的程序編譯功能。在Linux系統(tǒng)下,C語
言編寫的程序代碼一般需要通過gcc來編譯成可執(zhí)行程序。
1.2.1 gcc編譯器簡(jiǎn)介
Linux系統(tǒng)下的gcc編譯器(GNU C Compiler)是一個(gè)功能強(qiáng)大、性能優(yōu)越的編譯器。gcc
支持多種平臺(tái)的編譯,是Linux系統(tǒng)自由軟件的代表作品。gcc本來只是C編譯器的,但是后
來發(fā)展為可在多種硬體平臺(tái)上編譯出可執(zhí)行程序的超級(jí)編譯器。各種硬件平臺(tái)對(duì)gcc的支持
使得其執(zhí)行效率與一般的編譯器相比平均效率要高20%~30%。gcc編譯器能將C、C++源程
序、匯程語言和目標(biāo)程序進(jìn)行編譯鏈接成可執(zhí)行文件。通過支持make工具,gcc可以實(shí)施項(xiàng)
目管理和批量編譯。
經(jīng)過多年的發(fā)展,gcc已經(jīng)發(fā)生了很大的變化。gcc已經(jīng)不僅僅能支持C語言,還支持Ada
語言、C++語言、Java語言、Objective C語言、Pascal語言、COBOL語言等更多的語言集的編
譯。gcc幾乎支持所有的硬件平臺(tái),使得gcc對(duì)于特定的平臺(tái)可以編譯出更高效的機(jī)器碼。
gcc在編譯一個(gè)程序時(shí),一般需要完成預(yù)處理(preprocessing)、編譯(compilation)、匯
編(assembly)和鏈接(linking)過程。使用gcc編譯C程序時(shí),這些過程是使用默認(rèn)的設(shè)置
自動(dòng)完成的,但是用戶可以對(duì)這些過程進(jìn)行設(shè)置,控制這些操作的詳細(xì)過程。
1.2.2 gcc對(duì)源程序擴(kuò)展名的支持
擴(kuò)展名指的是文件名中最后一個(gè)點(diǎn)的這個(gè)點(diǎn)以后的部分。例如下面是一個(gè)C程序源文件
的擴(kuò)展名。
5.1.c
那么這個(gè)文件的文件名是“5.1.c”,擴(kuò)展名是“.c”。通常來說,源文件的擴(kuò)展名標(biāo)識(shí)源文
件所使用的編程語言。例如C程序源文件的擴(kuò)展名一般是“.c”。對(duì)編譯器來說,擴(kuò)展名控制
著缺省語言的設(shè)定。在默認(rèn)情況下,gcc通過文件擴(kuò)展名來區(qū)分源文件的語言類型。然后根據(jù)
這種語言類型進(jìn)行不同的編譯。gcc對(duì)源文件的擴(kuò)展名約定如下所示。
.c為擴(kuò)展名的文件,為C語言源代碼文件。
.a為擴(kuò)展名的文件,是由目標(biāo)文件構(gòu)成的庫(kù)文件。
.C,.cc或.cpp為擴(kuò)展名的文件,標(biāo)識(shí)為C++源代碼文件。
.h為擴(kuò)展名的文件,說明文件是程序所包含的頭文件。
.i為擴(kuò)展名的文件,標(biāo)識(shí)文件是已經(jīng)預(yù)處理過的C源代碼文件,一般為中間代碼文件。
.ii為擴(kuò)展名的文件,是已經(jīng)預(yù)處理過的C++源代碼文件,同上也是中間代碼文件。
.o為擴(kuò)展名的文件,是編譯后的目標(biāo)文件,源文件生成的中間目標(biāo)文件。
.s為擴(kuò)展名的文件,是匯編語言源代碼文件。
.S為擴(kuò)展名的文件,是經(jīng)過預(yù)編譯的匯編語言源代碼文件。
.o為擴(kuò)展名的文件,是編譯以后的程序目標(biāo)文件(Object file),目標(biāo)文件經(jīng)過連接成
可執(zhí)行文件
此外,對(duì)于gcc編譯器提供兩種顯示的編譯命令,分別對(duì)應(yīng)于編譯C和C++源程序的
編譯命令。
1.3 C程序的編譯
本章以一個(gè)實(shí)例講述如何用gcc編譯C程序。在編譯程序之前,需要用VIM編寫一個(gè)簡(jiǎn)
單的C程序。在編譯程序時(shí),可以對(duì)gcc命令進(jìn)行不同的設(shè)置。
1.3.1編寫第一個(gè)C程序
本節(jié)將編寫第一個(gè)C程序。程序?qū)崿F(xiàn)一句文本的輸出和判斷兩個(gè)整數(shù)的大小關(guān)系。本書
中編寫程序使用的編輯器是VIM。程序編寫步驟如下所示。
打開系統(tǒng)的終端。單擊“主菜單”|“系統(tǒng)工具”|“終端”命令,打開一個(gè)系統(tǒng)
終端。
在終端中輸入下面的命令,在用戶根目錄“root”中建立一個(gè)目錄。
mkdir c
在終端界面中輸入“vim”命令,然后按“Enter”鍵,系統(tǒng)會(huì)啟動(dòng)VIM。
在VIM中按“i”鍵,進(jìn)入到插入模式。然后在VIM中輸入下面的程序代碼。
#include<stdio.h>
int max(int i,int j)
{
if(i>j)
{
return(i);
}
else
{
return(j);
}
}
void main()
{
int i,j,k;
i=3;
j=5;
printf("hello,Linux./n”);
k=max(i,j);
printf("%d/n",k);
}
代碼輸入完成以后,按“Esc”鍵,返回到普通模式。然后輸入下面的命令,保存文件。
:w/root/c/a.c
這時(shí),VIM會(huì)把輸入的程序保存到c目錄下的文件a.c中。
再輸入“:q”命令,退出VIM。這時(shí),已經(jīng)完成了這個(gè)C程序的編寫。
1.3.2用gcc編譯程序
上面編寫的C程序,只是一個(gè)源代碼文件,還不能作為程序來執(zhí)行。需要用gcc將這個(gè)
源代碼文件編譯成可執(zhí)行文件。編譯文件的步驟如下所示。
打開系統(tǒng)的終端。單擊“主菜單”|“系統(tǒng)工具”|“終端”命令,打開一個(gè)系統(tǒng)
終端。這時(shí)進(jìn)入的目錄是用戶根目錄“/root”。然后輸入下面的命令,進(jìn)入到c目錄。
cd c
上一節(jié)編寫的程序就存放在這個(gè)目錄中。輸入“l(fā)s”命令可以查看這個(gè)目錄下的文件。
顯示的結(jié)果如下所示。
輸入下面的命令,將這個(gè)代碼文件編譯成可執(zhí)行程序。
gcc a.c
查看已經(jīng)編譯的文件。在終端中輸入“l(fā)s”命令,顯示的結(jié)果如下所示。
a.c a.out
輸入下面的命令對(duì)這個(gè)程序添加可執(zhí)行權(quán)限。
chmod+x a.out
輸入下面的命令,運(yùn)行這個(gè)程序。
./a.out
程序的運(yùn)行結(jié)果如下所示。
hello,Linux.
5
從上面的操作可知,用gcc可以將一個(gè)C程序源文件編譯成一個(gè)可執(zhí)行程序。編譯以
后的程序需要添加可執(zhí)行的權(quán)限才可以運(yùn)行。在實(shí)際操作中,還需要對(duì)程序的編譯進(jìn)行各
種設(shè)置。
1.3.3查看gcc的參數(shù)
gcc在編譯程序時(shí)可以有很多可選參數(shù)。在終端中輸入下面的命令,可以查看gcc的這些
可選參數(shù)。
gcc--help
在終端中顯示的gcc的可選參數(shù)如下所示。進(jìn)行程序編譯時(shí),可以設(shè)置下面的這些參數(shù)。
用法:gcc[選項(xiàng)]文件...
選項(xiàng):
-pass-exit-codes:在某一階段退出時(shí)返回最高的錯(cuò)誤碼
--help:顯示此幫助說明
--target-help:顯示目標(biāo)機(jī)器特定的命令行選項(xiàng)
-dumpspecs:顯示所有內(nèi)建spec字符串
-dumpversion:顯示編譯器的版本號(hào)
-dumpmachine:顯示編譯器的目標(biāo)處理器
-print-search-dirs:顯示編譯器的搜索路徑
-print-libgcc-file-name:顯示編譯器伴隨庫(kù)的名稱
-print-file-name=<庫(kù)>:顯示<庫(kù)>的完整路徑
-print-prog-name=<程序>:顯示編譯器組件<程序>的完整路徑
-print-multi-directory:顯示不同版本libgcc的根目錄
-print-multi-lib:顯示命令行選項(xiàng)和多個(gè)版本庫(kù)搜索路徑間的映射
-print-multi-os-directory:顯示操作系統(tǒng)庫(kù)的相對(duì)路徑
-Wa,<選項(xiàng)>:將逗號(hào)分隔的<選項(xiàng)>傳遞給匯編器
-Wp,<選項(xiàng)>:將逗號(hào)分隔的<選項(xiàng)>傳遞給預(yù)處理器
-Wl,<選項(xiàng)>:將逗號(hào)分隔的<選項(xiàng)>傳遞給鏈接器
-Xassembler<參數(shù)>:將<參數(shù)>傳遞給匯編器
-Xpreprocessor<參數(shù)>:將<參數(shù)>傳遞給預(yù)處理器
-Xlinker<參數(shù)>:將<參數(shù)>傳遞給鏈接器
-combine:將多個(gè)源文件一次性傳遞給匯編器
-save-temps:不刪除中間文件
-pipe:使用管道代替臨時(shí)文件
-time:為每個(gè)子進(jìn)程計(jì)時(shí)
-specs=<文件>:用<文件>的內(nèi)容覆蓋內(nèi)建的specs文件
-std=<標(biāo)準(zhǔn)>:指定輸入源文件遵循的標(biāo)準(zhǔn)
--sysroot=<目錄>:將<目錄>作為頭文件和庫(kù)文件的根目錄
-B<目錄>:將<目錄>添加到編譯器的搜索路徑中
-b<機(jī)器>:為gcc指定目標(biāo)機(jī)器(如果有安裝)
-V<版本>:運(yùn)行指定版本的gcc(如果有安裝)
-v:顯示編譯器調(diào)用的程序
-###:與-v類似,但選項(xiàng)被引號(hào)括住,并且不執(zhí)行命令
-E:僅作預(yù)處理,不進(jìn)行編譯、匯編和鏈接
-S:編譯到匯編語言,不進(jìn)行匯編和鏈接
-c:編譯、匯編到目標(biāo)代碼,不進(jìn)行鏈接
-o<文件>:輸出到<文件>
-x<語言>:指定其后輸入文件的語言。允許的語言包括c、c++、assembler等。
以-g、-f、-m、-O、-W或--param開頭的選項(xiàng)將由gcc自動(dòng)傳遞給其調(diào)用的不同子進(jìn)程。若要
向這些進(jìn)程傳遞其他選項(xiàng),必須使用-W<字母>選項(xiàng)。
1.3.4設(shè)置輸出的文件
在默認(rèn)情況下,gcc編譯出的程序?yàn)楫?dāng)前目錄下的文件a.out。-o參數(shù)可以設(shè)置輸出的目
標(biāo)文件。例如下面的命令,可以設(shè)置將代碼編譯成可執(zhí)行程序do。
gcc a.c-o do
也可以設(shè)置輸出目錄文件為不同的目錄。例如下面的命令,是將目錄文件設(shè)置成/tmp目
錄下的文件do。
gcc a.c-o/tmp/do
輸入下面的命令,查看生成的目錄文件。結(jié)果如下所示,在編譯程序時(shí)生成的目錄為/tmp
目錄下的文件do。
-rwxrwxr-x 1 root root 5109 12-28 13:33/tmp/do
1.3.5查看編譯過程
參數(shù)-v可以查看程序的編譯過程和顯示已經(jīng)調(diào)用的庫(kù)。輸入下面的命令,在編譯程序時(shí)
輸出編譯過程。
gcc-v a.c
顯示的結(jié)果如下所示。
使用內(nèi)建specs。
目標(biāo):i386-redhat-linux
配置為:../configure--prefix=/usr--mandir=/usr/share/man
--infodir=/usr/share/info--enable-shared--enable-threads=posix
--enable-checking=release--with-system-zlib--enable-__cxa_atexit
--disable-libunwind-exceptions
--enable-languages=c,c++,objc,obj-c++,java,fortran,ada
--enable-java-awt=gtk--disable-dssi--enable-plugin
--host=i386-redhat-linux
線程模型:posix
gcc版本4.1.2 20070925(Red Hat 4.1.2-33)
/usr/libexec/gcc/i386-redhat-linux/4.1.2/cc1
-quiet-v a.c-quiet-dumpbase a.c-mtune=generic-auxbase a-version
-o/tmp/cc8P7rzb.s
忽略不存在的目錄“/usr/lib/gcc/i386-redhat-linux/4.1.2/../../../../i386-
redhat-linux/include”
#include"..."搜索從這里開始:
#include<...>搜索從這里開始:
/usr/local/include
/usr/lib/gcc/i386-redhat-linux/4.1.2/include
/usr/include
搜索列表結(jié)束。
GNU C版本4.1.2 20070925(Red Hat 4.1.2-33)(i386-redhat-linux)
由GNU C版本4.1.2 20070925(Red Hat 4.1.2-33)編譯。
GGC準(zhǔn)則:--param ggc-min-expand=64--param ggc-min-heapsize=64394
Compiler executable checksum:ab322ce5b87a7c6c23d60970ec7b7b31
a.c:In function‘main’:
a.c:16:警告:‘main’的返回類型不是‘int’
as-V-Qy-o/tmp/ccEFPrYh.o/tmp/cc8P7rzb.s
GNU assembler version 2.17.50.0.18(i386-redhat-linux)using BFD version
version 2.17.50.0.18-1 20070731
/usr/libexec/gcc/i386-redhat-linux/4.1.2/collect2
--eh-frame-hdr--build-id-m elf_i386--hash-style=gnu-dynamic-linker
/lib/ld-linux.so.2/usr/lib/gcc/i386-redhat-linux/4.1.2/../../../crt1.o
從顯示的編譯過程可知,gcc自動(dòng)加載了系統(tǒng)的默認(rèn)配置,調(diào)用系統(tǒng)的庫(kù)函數(shù)完成了程序
的編譯過程。
1.3.6設(shè)置編譯的語言
gcc可以對(duì)多種語言編寫的源代碼。如果源代碼的文件擴(kuò)展名不是默認(rèn)的擴(kuò)展名,gcc就
無法編譯這個(gè)程序。可以用-x選擇來設(shè)置程序的語言。可以用下面的步驟來練習(xí)這一操作。
輸入下面的命令,將C程序文件復(fù)制一份。
cp a.c a.u
復(fù)制出的文件a.u是一個(gè)C程序文件,但擴(kuò)展名不是默認(rèn)的擴(kuò)展名。這時(shí)輸入下面的
命令編譯這個(gè)程序。
gcc a.u
顯示的結(jié)果如下所示,表明文件的格式不能識(shí)別。
a.u:file not recognized:File format not recognized
collect2:ld返回1
這時(shí),用-x參數(shù)設(shè)置編譯的語言,命令如下所示。這樣就可以正常地編譯文件a.u。
gcc-x'c'a.u
需要注意的是,這里的c需要用單引號(hào)擴(kuò)起來。當(dāng)編譯擴(kuò)展名不是.c的C程序時(shí),需要
使用-x參數(shù)。
1.3.7-asci設(shè)置ANSIC標(biāo)準(zhǔn)
ANSIC是American National Standards Institute(ANSI:美國(guó)標(biāo)準(zhǔn)協(xié)會(huì))出版的C語言
標(biāo)準(zhǔn)。使用這種標(biāo)準(zhǔn)的C程序可以在各種編譯器和系統(tǒng)下運(yùn)行通過。gcc可以編譯ANSIC的
程序,但是gcc中的很多標(biāo)準(zhǔn)并不被ANSIC所支持。在gcc編譯程序時(shí),可以用-ansic來設(shè)置
程序使用ANSIC標(biāo)準(zhǔn)。例如下面的命令,在設(shè)置程序編譯時(shí),用ANSIC標(biāo)準(zhǔn)進(jìn)行編譯。
gcc-asci a.out
5.3.8
1.3.8 g++編譯C++程序
gcc可以編譯C++程序。編譯C程序和C++程序時(shí),使用的是不同的命令。編譯C++程
序時(shí),使用的命令是g++。該命令的使用方法與gcc是相似的。下面是使用g++命令編譯C++
程序的實(shí)例。
下面是一個(gè)C++程序的代碼,實(shí)現(xiàn)與1.3.1節(jié)程序同樣的功能。C++程序的代碼與C程序
的代碼非常相似。
#include<iostream>
int max(int i,int j)
{
if(i>j)
{
return(i);
}
else
{
return(j);
}
}
int main()
{
int i,j,k;
i=3;
j=5;
printf("hello,Linux./n");
k=max(i,j);
printf("%d/n",k);
return(0);
}
輸入下面的命令,編譯這個(gè)C++程序。
g++5.2.cpp–o 5.2.out
輸入下面的命令,對(duì)這個(gè)程序添加可執(zhí)行權(quán)限。
chmod+x 5.2.ou
輸入下面的命令,運(yùn)行這個(gè)程序,程序的代碼如下所示。
hello,Linux.
5
從結(jié)果可知,這個(gè)程序與1.3.1節(jié)中的程序運(yùn)行結(jié)果是相同的。
1.4編譯過程的控制
編譯過程指的是gcc對(duì)一個(gè)程序進(jìn)行編譯時(shí)完成的內(nèi)部處理和步驟。編譯程序時(shí)會(huì)自動(dòng)
完成預(yù)處理(Preprocessing)、編譯(Compilation)、匯編(Assembly)和鏈接(Linking)4個(gè)
步驟。本節(jié)將講解如何對(duì)這4個(gè)步驟進(jìn)行控制。
1.4.1編譯過程簡(jiǎn)介
gcc把一個(gè)程序的源文件,編譯成一個(gè)可執(zhí)行文件,中間包括很多復(fù)雜的過程。可以用圖
1-1來表示編譯中4個(gè)步驟的作用和關(guān)系。
源文件
匯編文件
目標(biāo)文件
可執(zhí)行文件
使用-E選項(xiàng)進(jìn)行預(yù)處理
使用-S選項(xiàng)匯編
-c選項(xiàng)生成目標(biāo)文件
鏈接目標(biāo)文件和庫(kù)
生成可執(zhí)行文件
預(yù)處理文件
圖1-1 gcc編譯源文件到可執(zhí)行文件的過程
在4個(gè)過程中,每一個(gè)操作都完成了不同的功能。編譯過程的功能如下所示。
預(yù)處理:在預(yù)處理階段,主要完成對(duì)源代碼中的預(yù)編譯語句(如宏定義define等)和
文件包含進(jìn)行處理。需要完成的工作是對(duì)預(yù)編譯指令進(jìn)行替換,把包含文件放置到需
要編譯的文件中。完成這些工作后,會(huì)生成一個(gè)非常完整的C程序源文件。
編譯:gcc對(duì)預(yù)處理以后的文件進(jìn)行編譯,生成以.s為后綴的匯編語言文件。該匯編語
言文件是編譯源代碼得到的匯編語言代碼,接下來交給匯編過程進(jìn)行處理。匯編語言
是一種比C語言更低級(jí)的語言,直接面對(duì)硬盤進(jìn)行操作。程序需要編譯成匯編指令以
后再編譯成機(jī)器代碼。
匯編:匯編過程是處理匯編語言的階段,主要調(diào)用匯編處理程序完成將匯編語言匯編
成二進(jìn)制機(jī)器代碼的過程。通常來說,匯編過程是將.s的匯編語言代碼文件匯編為.o
的目標(biāo)文件的過程。所生成的目標(biāo)文件作為下一步鏈接過程的輸入文件。
鏈接:鏈接過程就是將多個(gè)匯編生成的目標(biāo)文件以及引用的庫(kù)文件進(jìn)行模塊鏈接生成
一個(gè)完整的可執(zhí)行文件。在鏈接階段,所有的目標(biāo)文件被安排在可執(zhí)行程序中的適當(dāng)
的位置。同時(shí),該程序所調(diào)用到的庫(kù)函數(shù)也從各自所在的函數(shù)庫(kù)中鏈接到程序中。經(jīng)
過了這個(gè)過程以后,生成的文件就是可執(zhí)行的程序。
1.4.2控制預(yù)處理過程
參數(shù)-E可以完成程序的預(yù)處理工作而不進(jìn)行其他的編譯工作。下面的命令,可以將本章
編寫的程序進(jìn)行預(yù)處理,然后保存到文件a.cxx中。
gcc-E-o a.cxx a.c
輸入下面的命令,查看經(jīng)過預(yù)處理以后的a.cxx文件。
vim a.cxx
可以發(fā)現(xiàn),文件a.cxx約有800行代碼。程序中默認(rèn)包含的頭文件已經(jīng)被展開寫到到這個(gè)
預(yù)處理文件
匯編文件
目標(biāo)文件
可執(zhí)行文件
鏈接目標(biāo)文件和庫(kù)
生成可執(zhí)行文件
-c選項(xiàng)生成目標(biāo)文件
使用-s選項(xiàng)匯編
使用-E選項(xiàng)進(jìn)行預(yù)處理
文件中。顯示的文件a.cxx前幾行代碼如下所示。可見,在程序編譯時(shí),需要調(diào)用非常多的頭
文件和系統(tǒng)庫(kù)函數(shù)。
#1"a.c"
#1"<built-in>"
#1"<command line>"
#1"a.c"
#1"/usr/include/stdio.h"1 3 4
#28"/usr/include/stdio.h"3 4
#1"/usr/include/features.h"1 3 4
#335"/usr/include/features.h"3 4
#1"/usr/include/sys/cdefs.h"1 3 4
#360"/usr/include/sys/cdefs.h"3 4
#1"/usr/include/bits/wordsize.h"1 3 4
#361"/usr/include/sys/cdefs.h"2 3 4
#336"/usr/include/features.h"2 3 4
#359"/usr/include/features.h"3 4
#1"/usr/include/gnu/stubs.h"1 3 4
#1"/usr/include/bits/wordsize.h"1 3 4
#5"/usr/include/gnu/stubs.h"2 3 4
#1"/usr/include/gnu/stubs-32.h"1 3 4
#8"/usr/include/gnu/stubs.h"2 3 4
#360"/usr/include/features.h"2 3 4
#29"/usr/include/stdio.h"2 3 4
1.4.3生成匯編代碼
參數(shù)-S可以控制gcc在編譯C程序時(shí)只生成相應(yīng)的匯編程序文件,而不繼續(xù)執(zhí)行后面的
編譯。下面的命令,可以將本章中的C程序編譯成一個(gè)匯編程序。
gcc-S-o a.s a.c
輸入下面的命令,查看匯編文件a.s。可以發(fā)現(xiàn)這個(gè)文件一共有60行代碼。這些代碼是
這個(gè)程序的匯編指令。部分匯編程序代碼如下所示。
.file"a.c"
.text
.globl max
.type max,@function
max:
pushl%ebp
movl%esp,%ebp
subl$4,%esp
movl 8(%ebp),%eax
cmpl 12(%ebp),%eax
jle.L2
movl 8(%ebp),%eax
movl%eax,-4(%ebp)
jmp.L4
.L2:
movl 12(%ebp),%eax
movl%eax,-4(%ebp)
.L4:
movl-4(%ebp),%eax
leave
ret
.size max,.-max
.section.rodata
.LC0:
.string"hello,Linux."
.LC1:
.string"%d/n"
.text
.globl main
.type main,@function
main:
leal 4(%esp),%ecx
andl$-16,%esp
.......
popl%ebp
leal-4(%ecx),%esp
ret
.size main,.-main
.ident"GCC:(GNU)4.1.2 20070925(Red Hat 4.1.2-33)"
.section.note.GNU-stack,"",@progbits
1.4.4生成目標(biāo)代碼
參數(shù)-c可以使得gcc在編譯程序時(shí)只生成目錄代碼而不生成可執(zhí)行程序。輸入下面的命
令,將本章中的程序編譯成目錄代碼。
gcc-c-o a.o a.c
輸入下面的命令,查看這個(gè)目錄代碼的信息。
file a.o
顯示文件a.o的結(jié)果如下所示,顯示文件a.o是一個(gè)可重定位的目標(biāo)代碼文件。
a.o:ELF 32-bit LSB relocatable,Intel 80386,version 1(SYSV),not stripped
1.4.5鏈接生成可執(zhí)行文件
gcc可以把上一步驟生成的目錄代碼文件生成一個(gè)可執(zhí)行文件。在終端中輸入下面的命令。
gcc a.o-o aa.out
這時(shí)生成一個(gè)可執(zhí)行文件aa.out。輸入下面的命令查看這個(gè)文件的信息。
file aa.out
顯示的結(jié)果如下所示,表明這個(gè)文件是可在Linux系統(tǒng)下運(yùn)行的程序文件。
aa.out:ELF 32-bit LSB executable,Intel 80386,version 1(SYSV),dynamically
linked(uses shared libs),for GNU/Linux 2.6.9,not stripped
1.5 gdb調(diào)試程序
所謂調(diào)試,指的是對(duì)編好的程序用各種手段進(jìn)行查錯(cuò)和排錯(cuò)的過程。進(jìn)行這種查錯(cuò)處理
時(shí),并不僅僅是運(yùn)行一次程序檢查結(jié)果,而是對(duì)程序的運(yùn)行過程、程序中的變量進(jìn)行各種分
析和處理。本節(jié)將講解使用gdb進(jìn)行程序的調(diào)試。
1.5.1 gdb簡(jiǎn)介
gdb是一個(gè)功能強(qiáng)大的調(diào)試工具,可以用來調(diào)試C程序或C++程序。在使用這個(gè)工具進(jìn)
行程序調(diào)試時(shí),主要使用gdb進(jìn)行下面5個(gè)方面的操作。
啟動(dòng)程序:在啟動(dòng)程序時(shí),可以設(shè)置程序運(yùn)行環(huán)境。
設(shè)置斷點(diǎn):斷點(diǎn)就是可以在程序設(shè)計(jì)時(shí)暫停程序運(yùn)行的標(biāo)記。程序會(huì)在斷點(diǎn)處停止,
用戶便于查看程序的運(yùn)行情況。這里的斷點(diǎn)可以是行數(shù)、程序名稱或條件表達(dá)式。
查看信息:在斷點(diǎn)停止后,可以查看程序的運(yùn)行信息和顯示程序變量的值。
分步運(yùn)行:可以使程序一個(gè)語句一個(gè)語句的執(zhí)行,這時(shí)可以及時(shí)地查看程序的信息。
改變環(huán)境:可以在程序運(yùn)行時(shí)改變程序的運(yùn)行環(huán)境和程序變量。
1.5.2在程序中加入調(diào)試信息
為了使用gdb進(jìn)行程序調(diào)試,需要在編譯程序中加入供gdb使用的調(diào)試信息。方法是在
編譯程序時(shí)使用一個(gè)-g參數(shù)。在終端中輸入下面的命令,在編譯程序時(shí)加入調(diào)試信息。
gcc-g-o a.debug a.c
這時(shí),編譯程序a.c,生成一個(gè)a.bedug的可執(zhí)行程序。這個(gè)可執(zhí)行程序中加入了供調(diào)試
所用的信息。
1.5.3啟動(dòng)gdb
在調(diào)試文件以前,需要啟動(dòng)gdb。在終端中輸入下面的命令。
gdb
這時(shí),gdb的啟動(dòng)信息如下所示。這些提示顯示了gdb的版本和版權(quán)信息。
GNU gdb Red Hat Linux(6.6-35.fc8rh)
Copyright(C)2006 Free Software Foundation,Inc.
GDB is free software,covered by the GNU General Public License,and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type"show copying"to see the conditions.
There is absolutely no warranty for GDB.Type"show warranty"for details.
This GDB was configured as"i386-redhat-linux-gnu".
(gdb)
1.5.4在gdb中加載需要調(diào)試的程序
使用gdb調(diào)試一個(gè)程序之前,需要加載這個(gè)程序。加載程序的命令是file。在(gdb)提示
符后面輸入下面的命令加載程序a.debug。
file a.debug
命令的運(yùn)行結(jié)果如下所示,顯示已經(jīng)加載了這個(gè)文件,并且使用了系統(tǒng)庫(kù)文件。
Reading symbols from/root/c/a.debug...done.
Using host libthread_db library"/lib/libthread_db.so.1".
1.5.5在gdb中查看代碼
用gcc命令編譯程序加入了-g命令以后,編譯后的a.debug程序中加入了斷點(diǎn)。可以用list
命令顯示程序的源代碼和斷點(diǎn)。下面的步驟是查看加入斷點(diǎn)以后的代碼。
在(gdb)提示符后面輸入下面的命令。
list 1
這時(shí),gdb會(huì)顯示第一個(gè)斷點(diǎn)以前的代碼。顯示的代碼如下所示。
1#include<stdio.h>
2
3 int max(int i,int j)
4{
5 if(i>j)
6{
7 return(i);
8}
9 else
10{
(gdb)
這時(shí),按“Enter”鍵,顯示下一個(gè)斷點(diǎn)以前的代碼。結(jié)果如下所示。
11 return(j);
12}
13}
14
15 void main()
16{
17 int i,j,k;
18 i=3;
19 j=5;
20 printf("hello,Linux./n");
(gdb)
按“Enter”鍵,顯示下一個(gè)斷點(diǎn)以前的代碼。結(jié)果如下所示。
21 k=max(i,j);
22 printf("%d/n",k);
23}
(gdb)
1.5.6在程序中加入斷點(diǎn)
程序會(huì)運(yùn)行到斷點(diǎn)的位置停止下來,等待用戶處理信息或者查看中間變量。如果自動(dòng)設(shè)
置的斷點(diǎn)不能滿足調(diào)試要求,可以用break命令增加程序的斷點(diǎn)。例如需要在程序的第6行增
加一個(gè)斷點(diǎn),可以輸入下面的命令。
break 6
這時(shí)gdb顯示的結(jié)果如下所示。
Breakpoint 1 at 0x8048402:file a.c,line 6.
輸入下面的命令,在程序的第18行、19行、21行增加斷點(diǎn)。
break 18
break 19
break 21
1.5.7查看斷點(diǎn)
命令info breakpoint可以查看程序中設(shè)置的斷點(diǎn)。輸入“info breakpoint”命令,結(jié)果如
下所示。顯示程序中所有的斷點(diǎn)。
1 breakpoint keep y 0x08048402 in max at a.c:6
2 breakpoint keep y 0x08048426 in main at a.c:18
3 breakpoint keep y 0x0804842d in main at a.c:19
4 breakpoint keep y 0x08048440 in main at a.c:21
加上相應(yīng)的斷點(diǎn)編號(hào),可以查看這一個(gè)斷點(diǎn)的信息。例如下面的命令就是查看第二個(gè)斷點(diǎn)。
info breakpoint 2
顯示的結(jié)果如下所示。
2 breakpoint keep y 0x08048426 in main at a.c:18
1.5.8運(yùn)行程序
gdb中的run命令可以使這個(gè)程序以調(diào)試的模式運(yùn)行。下面的步驟是分步運(yùn)行程序,對(duì)程
序進(jìn)行調(diào)試。
在(ddb)提示符后輸入“run”命令,顯示的結(jié)果如下所示。
Starting program:/root/c/a.debug
warning:Missing the separate debug info file:
/usr/lib/debug/.build-id/ac/2eeb206486bb7315d6ac4cd64de0cb50838ff6.debug
warning:Missing the separate debug info file:
/usr/lib/debug/.build-id/ba/4ea1118691c826426e9410cafb798f25cefad5.debug
Breakpoint 2,main()at a.c:18
18 i=3;
結(jié)果顯示了程序中的異常,并將異常記錄到了系統(tǒng)文件中。然后在程序的第二個(gè)斷
點(diǎn)的位置第18行停下。
這時(shí)輸入“next”命令,程序會(huì)在下一行停下,結(jié)果如下所示。
19 j=5;
輸入“continue”命令,程序會(huì)在下一個(gè)斷點(diǎn)的位置停下。結(jié)果如下所示。
Continuing.
Breakpoint 3,main()at a.c:19
21 k=max(i,j);
輸入“continue”命令,程序運(yùn)行到結(jié)束。結(jié)果如下所示,表明程序已經(jīng)運(yùn)行完畢正
常退出。
5
Program exited with code 02.
step命令與next命令的作用相似,對(duì)程序?qū)崿F(xiàn)單步運(yùn)行。不同之處是,在遇上函數(shù)
調(diào)用時(shí),step函數(shù)可以進(jìn)行到函數(shù)內(nèi)部。而next函數(shù)只是一步完成函數(shù)的調(diào)用。
1.5.9變量的查看
print命令可以在程序的運(yùn)行中查看一個(gè)變量的值。本節(jié)將用下面的步驟來講解變量的查
看方法。
輸入下面的命令,運(yùn)行程序。
run
程序在第一個(gè)斷點(diǎn)位置停下。顯示的結(jié)果如下所示。
Breakpoint 2,main()at a.c:18
18 i=3;
程序進(jìn)入第18行之前停下,并沒有對(duì)i進(jìn)行賦值。可以用下面的命令來查看i的值。
print i
顯示的結(jié)果如下所示,表示i現(xiàn)在只是一個(gè)任意值。
$5=-1076190040
輸入下面的命令,使程序運(yùn)行一步。
step
顯示的結(jié)果如下所示。
19 j=5;
這時(shí)程序在19行以前停下,這時(shí)輸入下面的命令,查看i的值。
print i
這時(shí)顯示的i的結(jié)果如下所示。表明i已經(jīng)賦值為3。
$6=3
這時(shí)輸入“step”命令,再次輸入“step”命令,顯示的結(jié)果如下所示。
21 k=max(i,j);
這時(shí)輸入“step”命令,會(huì)進(jìn)入到子函數(shù)中,結(jié)果如下所示。這時(shí),顯示了傳遞給函
數(shù)的變量和值。
max(i=3,j=5)at a.c:5
5 if(i>j)
這時(shí),輸入“step”命令,顯示的結(jié)果如下所示,表明函數(shù)會(huì)返回變量j。
11 return(j);
輸入下面的命令,查看j的值。
print j
顯示的結(jié)果如下所示,表明j的值為5。
$7=5
這時(shí)再運(yùn)行兩次“step”命令,顯示的結(jié)果如下所示。
22 printf("%d/n",k);
這時(shí),輸入下面的命令,查看k的值。
print k
顯示的結(jié)果如下所示,表明k的值為5。
$8=5
17完成了程序的調(diào)試運(yùn)行以后,輸入“q”命令,退出gdb。
1.6程序調(diào)試實(shí)例
本節(jié)講解一個(gè)程序調(diào)試實(shí)例。先編寫一個(gè)程序,在程序運(yùn)行時(shí),發(fā)現(xiàn)結(jié)果與預(yù)想結(jié)果有
些不同。然后用gdb工具進(jìn)行調(diào)試,通過對(duì)單步運(yùn)行和變量的查看,查找出程序的錯(cuò)誤。
1.6.1編寫一個(gè)程序
本節(jié)將編寫一個(gè)程序,要求程序運(yùn)行時(shí)可以顯示下面的結(jié)果。
1+1=2
2+1=3 2+2=4
3+1=4 3+2=5 3+3=6
4+1=5 4+2=6 4+3=7 4+4=8
很明顯,這個(gè)程序是通過兩次循環(huán)與一次判斷得到的。程序中需要定義三個(gè)變量。下面
用這個(gè)思路來編寫這個(gè)程序。
打開一個(gè)終端。在終端中輸入“vim”命令,打開VIM。
在VIM中按“i”鍵,進(jìn)入到插入模式。然后在VIM中輸入下面的代碼。
#include<stdio.h>
main()
{
int i,j,k;
for(i=1;i<=4;i++)
{
for(j=1;j<=4;j++);
{
if(i>=j)
{
k=i+j;
printf("%d+%d=%d",i,j,k);
}
}
printf("/n");
}
}
在VIM中按“Esc”鍵,返回到普通模式。然后輸入下面的命令,保存這個(gè)文件。
:w/root/c/test.c
輸入“:q”命令退出VIM。很容易發(fā)現(xiàn),在第二個(gè)循環(huán)后
1.6.2編譯文件
本節(jié)將對(duì)上一節(jié)編寫的程序進(jìn)行編譯和運(yùn)行。在運(yùn)行程序時(shí),會(huì)發(fā)現(xiàn)程序有錯(cuò)誤。
在終端中輸入下面的命令,編譯這個(gè)程序。
gcc/root/c/test.c
程序可以正常編譯通過,輸入下面的命令,運(yùn)行這個(gè)程序。
/root/c/a.out
程序的顯示結(jié)果是4個(gè)空行,并沒有按照預(yù)想的要求輸出結(jié)果。
輸入下面的命令,對(duì)這個(gè)程序進(jìn)行編譯。在編譯加入-g參數(shù),為gdb調(diào)試做準(zhǔn)備。
gcc-g-o test.debug 6.2.c
這時(shí),程序可以正常編譯通過。輸出的文件是test.debug。這個(gè)文件中加入了文件調(diào)
試需要的信息。
1.6.3程序的調(diào)試
本節(jié)將講述使用gdb對(duì)上一節(jié)編寫的程序進(jìn)行調(diào)試,查找出程序中的錯(cuò)誤。
在終端中輸入“gdb”命令,進(jìn)入到gdb,顯示的結(jié)果如下所示。
GNU gdb Red Hat Linux(6.6-35.fc8rh)
Copyright(C)2006 Free Software Foundation,Inc.
GDB is free software,covered by the GNU General Public License,and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type"show copying"to see the conditions.
There is absolutely no warranty for GDB.Type"show warranty"for details.
This GDB was configured as"i386-redhat-linux-gnu".
導(dǎo)入文件。在gdb中輸入下面的命令。
file/root/c/test.debug
這時(shí)顯示的結(jié)果如下所示。表明已經(jīng)成功加載了這個(gè)文件。
Reading symbols from/root/c/test.debug...(no debugging symbols
found)...done.
Using host libthread_db library"/lib/libthread_db.so.1".
查看文件。在終端中輸入下面的命令。
list
顯示的文件查看結(jié)果如下所示。
1#include<stdio.h>
2
3 main()
4{
5 int i,j,k;
6 for(i=1;i<=4;i++)
7{
8 for(j=1;j<=4;j++);
9{
10 if(i>=j)
(gdb)
11{
12 k=i+j;
13 printf("%d+%d=%d",i,j,k);
14}
15}
16 printf("/n");
17}
18}
(gdb)
Line number 19 out of range;6.2.c has 18 lines.
在程序中加入斷點(diǎn)。從顯示的代碼可知,需要在第6行、第11行、第12行和第13
行加入斷點(diǎn)。在gdb中輸入下面的命令。
break 6
break 11
break 12
break 13
gdb顯示的添加斷點(diǎn)的結(jié)果如下所示。
Breakpoint 1 at 0x8048405:file 6.2.c,line 6.
Breakpoint 2 at 0x8048429:file 6.2.c,line 11.
Breakpoint 3 at 0x8048429:file 6.2.c,line 12.
Breakpoint 4 at 0x8048432:file 6.2.c,line 13.
輸入下面的命令,運(yùn)行這個(gè)程序。
run
運(yùn)行到第一個(gè)斷點(diǎn)顯示的結(jié)果如下所示。
Breakpoint 1,main()at 6.2.c:6
6 for(i=1;i<=4;i++)
輸入“step”命令,程序運(yùn)行一步,結(jié)果如下所示。
8 for(j=1;j<=4;j++);
這說明程序已經(jīng)進(jìn)入了for循環(huán)。這時(shí)輸入下面命令,查看i的值。
print i
顯示的結(jié)果如下所示。
$2=1
這時(shí)再輸入“step”命令,顯示的結(jié)果如下所示。
10 if(i>=j)
這時(shí)再輸入“step”命令,顯示的結(jié)果如下所示。
16 printf("/n");
這表明,在進(jìn)行j的for循環(huán)時(shí),沒有反復(fù)執(zhí)行循環(huán)體。這時(shí)再輸入“step”命令,
顯示的結(jié)果如下所示。
for(i=1;i<=4;i++)
這表明,程序正常的進(jìn)行了i的for循環(huán)。這是第二次執(zhí)行for循環(huán)。
17輸入“step”命令,顯示的結(jié)果如下所示。
8 for(j=1;j<=4;j++);
18這表明,程序執(zhí)行到for循環(huán)。這時(shí)再次輸入“step”命令,顯示的結(jié)果如下所示。
10 if(i>=j)
19輸入“step”命令,顯示的結(jié)果如下所示。
16 printf("/n");
20輸入“step”命令,顯示的結(jié)果如下所示。
6 for(i=1;i<=4;i++)
21這說明,程序正常的進(jìn)行了i的for循環(huán),但是沒有執(zhí)行j的for循環(huán)。這一定是j的
for循環(huán)語句有問題。這時(shí)就不難發(fā)現(xiàn)j的for循環(huán)后面多了一個(gè)分號(hào)。
22輸入“q”命令,退出gdb。
1.6.4 gdb幫助的使用
gdb有非常多的命令。輸入“help”命令可以顯示這些命令的幫助信息。本節(jié)將講解幫助
信息的使用。
在gdb輸入“help”命令,顯示的幫助信息如下所示。
List of classes of commands:
aliases--Aliases of other commands
breakpoints--Making program stop at certain points
data--Examining data
files--Specifying and examining files
internals--Maintenance commands
obscure--Obscure features
running--Running the program
stack--Examining the stack
status--Status inquiries
support--Support facilities
tracepoints--Tracing of program execution without stopping the program
user-defined--User-defined commands
Type"help"followed by a class name for a list of commands in that class.
Type"help all"for the list of all commands.
Type"help"followed by command name for full documentation.
Type"apropos word"to search for commands related to"word".
Command name abbreviations are allowed if unambiguous.
上面的幫助信息顯示,輸入“help all”會(huì)輸出所有幫助信息。
在“help”命令后面加上一個(gè)命令名稱,可以顯示這個(gè)命令的幫助信息。例如輸入
“help file”,顯示的file命令幫助信息如下所示。
Use FILE as program to be debugged.
It is read for its symbols,for getting the contents of pure memory,
and it is the program executed when you use the`run'command.
If FILE cannot be found as specified,your execution directory path
($PATH)is searched for a command of that name.
No arg means to have no executable file and no symbols.
1.7 gdb常用命令
除了前面講述的gdb命令以外,gdb還有很多種命令。這些命令可以完成程序調(diào)試的各
種功能。其他的常用命令含義如下所示。
backtrace:顯示程序中的當(dāng)前位置和表示如何到達(dá)當(dāng)前位置的棧跟蹤(同義詞:where)。
breakpoint:在程序中設(shè)置一個(gè)斷點(diǎn)。
cd:改變當(dāng)前工作目錄。
clear:刪除剛才停止處的斷點(diǎn)。
commands:命中斷點(diǎn)時(shí),列出將要執(zhí)行的命令。
continue:從斷點(diǎn)開始繼續(xù)執(zhí)行。
delete:刪除一個(gè)斷點(diǎn)或監(jiān)測(cè)點(diǎn),也可與其他命令一起使用。
display:程序停止時(shí)顯示變量和表達(dá)式。
down:下移棧幀,使得另一個(gè)函數(shù)成為當(dāng)前函數(shù)。
frame:選擇下一條continue命令的幀。
info:顯示與該程序有關(guān)的各種信息。
info break:顯示當(dāng)前斷點(diǎn)清單,包括到達(dá)斷點(diǎn)處的次數(shù)等。
info files:顯示被調(diào)試文件的詳細(xì)信息。
info func:顯示所有的函數(shù)名稱。
info local:顯示當(dāng)函數(shù)中的局部變量信息。
info prog:顯示被調(diào)試程序的執(zhí)行狀態(tài)。
info var:顯示所有的全局和靜態(tài)變量名稱。
jump:在源程序中的另一點(diǎn)開始運(yùn)行。
kill:異常終止在gdb控制下運(yùn)行的程序。
list:列出相應(yīng)于正在執(zhí)行的程序的源文件內(nèi)容。
next:執(zhí)行下一個(gè)源程序行,從而執(zhí)行其整體中的一個(gè)函數(shù)。
print:顯示變量或表達(dá)式的值。
pwd:顯示當(dāng)前工作目錄。
pype:顯示一個(gè)數(shù)據(jù)結(jié)構(gòu)(如一個(gè)結(jié)構(gòu)或C++類)的內(nèi)容。
quit:退出gdb。
reverse-search:在源文件中反向搜索正規(guī)表達(dá)式。
run:執(zhí)行該程序。
search:在源文件中搜索正規(guī)表達(dá)式。
set variable:給變量賦值。
signal:將一個(gè)信號(hào)發(fā)送到正在運(yùn)行的進(jìn)程。
step:執(zhí)行下一個(gè)源程序行,必要時(shí)進(jìn)入下一個(gè)函數(shù)。
undisplay display:命令的反命令,不要顯示表達(dá)式。
until:結(jié)束當(dāng)前循環(huán)。
up:上移棧幀,使另一函數(shù)成為當(dāng)前函數(shù)。
watch:在程序中設(shè)置一個(gè)監(jiān)測(cè)點(diǎn)(即數(shù)據(jù)斷點(diǎn))。
whatis:顯示變量或函數(shù)類型。
1.8編譯程序常見的錯(cuò)誤與問題
在編寫程序時(shí),無論是邏輯上還是語法上,不可能一次做到完全正確。于是在編譯程序
時(shí),就會(huì)發(fā)生編譯錯(cuò)誤。本節(jié)將講述程序編譯時(shí)常見的錯(cuò)誤類型與處理方法。
1.8.1邏輯錯(cuò)誤與語法錯(cuò)誤
在編程時(shí),出現(xiàn)的錯(cuò)誤可能有邏輯錯(cuò)誤和語法錯(cuò)誤兩種。這兩種錯(cuò)誤的發(fā)生原因和處理
方法是不同的。本節(jié)將講述這兩種錯(cuò)誤的處理方法。
邏輯錯(cuò)誤指的是程序的設(shè)計(jì)思路發(fā)生了錯(cuò)誤。這種錯(cuò)誤在程序中是致命的,程序可能
正常編譯通過,但是結(jié)果是錯(cuò)誤的。當(dāng)程序正常運(yùn)行而結(jié)果錯(cuò)誤時(shí),一般都是編程的
思路錯(cuò)誤。這時(shí),需要重新考慮程序的運(yùn)算方法與數(shù)據(jù)處理流程是否正確。
語法錯(cuò)誤:語法錯(cuò)誤指的是程序的思路正確,但是在書寫語句時(shí),發(fā)生了語句錯(cuò)誤。
這種錯(cuò)誤一般是編程時(shí)不小心或是對(duì)語句的錯(cuò)誤理解造成的。在發(fā)生語句錯(cuò)誤時(shí),程
序一般不能正常編譯通過。這時(shí)會(huì)提示錯(cuò)誤的類型和錯(cuò)誤的位置,按照這些提示改正
程序的語法錯(cuò)誤即可完成錯(cuò)誤的修改。
1.8.2 C程序中的錯(cuò)誤與異常
C程序中的錯(cuò)誤,根據(jù)嚴(yán)重程序的不同,可以分為異常與警誤兩類。在編譯程序時(shí),這
兩種情況對(duì)編譯的影響是不同的,對(duì)錯(cuò)誤與異常的處理方式是不同的。
1.什么是異常
異常指的是代碼中輕微的錯(cuò)誤,這些錯(cuò)誤一般不會(huì)影響程序的正常運(yùn)行,但是不完全符
合編程的規(guī)范。在編譯程序時(shí),會(huì)產(chǎn)生一個(gè)“警告”,但是程序會(huì)繼續(xù)編譯。下面的程序會(huì)使
程序發(fā)生異常,在編譯時(shí)產(chǎn)生一個(gè)警告錯(cuò)誤。
在除法中,0作除數(shù)。
在開方運(yùn)算時(shí),對(duì)負(fù)數(shù)開平方。
程序的主函數(shù)沒有聲明類型。
程序的主函數(shù)沒有返回值。
程序中定義了一個(gè)變量,但是沒有使用這個(gè)變量。
變量的存儲(chǔ)發(fā)生了溢出。
2.什么是錯(cuò)誤
錯(cuò)誤指的是程序的語法出現(xiàn)問題,程序編譯不能正常完成,產(chǎn)生一個(gè)錯(cuò)誤信息。這時(shí)會(huì)
顯示錯(cuò)誤的類型與位置。根據(jù)這些信息可以對(duì)程序進(jìn)行修改。
1.8.3編譯中的警告提示
在編譯程序時(shí),如果發(fā)生了不嚴(yán)重的異常,會(huì)輸出一個(gè)錯(cuò)告錯(cuò)誤,然后完成程序的編譯。
例如下面的內(nèi)容是一個(gè)程序在編譯時(shí)產(chǎn)生的警告。
5.1.c:In function'main':
5.1.c:16:警告:‘main’的返回類型不是‘int’
5.1.c:18:警告:被零除
這些的含義如下所示。
(1)“In function'main':”表示發(fā)生的異常在main函數(shù)內(nèi)。
(2)“5.1.c:16:”表示發(fā)生異常的文件是5.1.c,位置是第16行。
(3)下面的信息是第16行的異常,表明程序的返回類型不正確。
‘main’的返回類型不是‘int’
(4)下面的警告信息表明程序的第18行有除數(shù)為0的錯(cuò)誤。
5.1.c:18:警告:被零除
1.8.4找不到包含文件的錯(cuò)誤
程序中的包含文件在系統(tǒng)或工程中一定要存在,否則程序編譯時(shí)會(huì)發(fā)生致命錯(cuò)誤。例如
下面的語句包含了一個(gè)不正確的頭文件。
#include<stdio1.h>
編譯程序時(shí),會(huì)發(fā)生錯(cuò)誤,錯(cuò)誤信息如下所示。
5.1.c:2:20:錯(cuò)誤:stdio2.h:沒有那個(gè)文件或目錄
1.8.5錯(cuò)誤地使用逗號(hào)
程序中逗號(hào)的含義是并列幾個(gè)內(nèi)容,形成某種算法或結(jié)構(gòu)。程序中如果錯(cuò)誤地使用逗號(hào),會(huì)
使程序在編譯時(shí)發(fā)生致命錯(cuò)誤。例如下面的代碼,是程序中的if語句后面有一個(gè)錯(cuò)誤的逗號(hào)。
int max(int i,int j)
{
if(i>j),
{
return(i);
}
else
{
return(j);
}
}
程序編譯時(shí)輸出的錯(cuò)誤信息如下所示。表明max函數(shù)中逗號(hào)前面的表達(dá)式有錯(cuò)誤,實(shí)際
上的錯(cuò)誤是多一個(gè)逗號(hào)。
5.1.c:In function‘max’:
5.1.c:4:錯(cuò)誤:expected expression before‘,’token
5.1.c:In function‘max’:
1.8.6括號(hào)不匹配錯(cuò)誤
程序中的引號(hào)、單引號(hào)、小括號(hào)、中括號(hào)、大括號(hào)等符號(hào)必須成對(duì)出現(xiàn)。這方面的錯(cuò)誤
會(huì)使程序發(fā)生符號(hào)不匹配的錯(cuò)誤。發(fā)生這種錯(cuò)誤后,編譯程序往往不能理解代碼的含義,也
不能準(zhǔn)確顯示錯(cuò)誤的位置,而是顯示表達(dá)式錯(cuò)誤。例如下面的代碼,在最后一行上了一個(gè)花
括號(hào)。
int max(int i,int j)
{
if(i>j)
{
return(i);
}
else
{
return(j);
}
編譯程序時(shí),會(huì)顯示下面的錯(cuò)誤信息。
5.1.c:22:錯(cuò)誤:expected declaration or statement at end of input
1.8.7小括號(hào)不匹配錯(cuò)誤
程序中的小括號(hào)一般在一行內(nèi)成對(duì)出現(xiàn)并且相匹配。小括號(hào)不匹配時(shí),程序發(fā)生致命錯(cuò)
誤。例如下面的代碼,第一行多了一個(gè)右半邊括號(hào)。
if(i>j))
{
return(i);
}
else
{
return(j);
}
編程程序時(shí),會(huì)發(fā)生下面的錯(cuò)誤。顯示括號(hào)前面有錯(cuò)誤,并且導(dǎo)致下面的else語句也有
錯(cuò)誤。
5.1.c:4:錯(cuò)誤:expected statement before‘)’token
5.1.c:8:錯(cuò)誤:expected expression before‘else’
1.8.8變量類型或結(jié)構(gòu)體聲明錯(cuò)誤
程序中的變量或結(jié)構(gòu)體的名稱必須正確,否則程序會(huì)發(fā)生未聲明的錯(cuò)誤。例如下面的代
碼,用一個(gè)不存在的類型來聲明一個(gè)變量。
ch a;
程序在運(yùn)行時(shí),會(huì)顯示出這個(gè)變量錯(cuò)誤,并且會(huì)顯示有其他的錯(cuò)誤。
5.1.c:17:錯(cuò)誤:‘ch’未聲明(在此函數(shù)內(nèi)第一次使用)
5.1.c:17:錯(cuò)誤:(即使在一個(gè)函數(shù)內(nèi)多次出現(xiàn),每個(gè)未聲明的標(biāo)識(shí)符在其
5.1.c:17:錯(cuò)誤:所在的函數(shù)內(nèi)只報(bào)告一次。)
5.1.c:17:錯(cuò)誤:expected‘;’before‘a(chǎn)’
1.8.9使用不存在的函數(shù)的錯(cuò)誤
如果程序引用了一個(gè)不存在的函數(shù),會(huì)使用程序發(fā)生嚴(yán)重的錯(cuò)誤。例如下面的代碼,引
用了一個(gè)不存在的函數(shù)add。
k=add(i,j);
程序顯示的錯(cuò)誤信息如下所示,表明在main函數(shù)中的add函數(shù)沒有定義。
/tmp/ccYQfDJy.o:In function`main':
5.1.c:(.text+0x61):undefined reference to`add'
collect2:ld返回1
5.8.10大小寫錯(cuò)誤
C程序?qū)Υa的大小寫是敏感的,不同的大小寫代表不同的內(nèi)容。例如下面的代碼,將
小寫的“int”錯(cuò)誤的寫成了“Int”。
Int t;
程序顯示的錯(cuò)誤信息如下所示,表明“Int”類型不存在或未聲明。發(fā)生這個(gè)錯(cuò)誤時(shí),會(huì)
輸出多行錯(cuò)誤提示。
5.1.c:16:錯(cuò)誤:‘Int’未聲明(在此函數(shù)內(nèi)第一次使用)
5.1.c:16:錯(cuò)誤:(即使在一個(gè)函數(shù)內(nèi)多次出現(xiàn),每個(gè)未聲明的標(biāo)識(shí)符在其
5.1.c:16:錯(cuò)誤:所在的函數(shù)內(nèi)只報(bào)告一次。)
5.1.c:16:錯(cuò)誤:expected‘;’before‘t’
1.8.11數(shù)據(jù)類型的錯(cuò)誤
程序中的某些運(yùn)算,必須針對(duì)相應(yīng)的數(shù)據(jù)類型,否則這個(gè)運(yùn)算會(huì)發(fā)生數(shù)據(jù)類型錯(cuò)誤。例
如下面的代碼,錯(cuò)誤地將兩個(gè)整型數(shù)進(jìn)行求余運(yùn)算。
float a,b;
a=a%b;
程序編譯時(shí),輸出下面的錯(cuò)誤,表明“%”運(yùn)算符的操作數(shù)無效。
5.1.c:19:錯(cuò)誤:雙目運(yùn)算符%操作數(shù)無效
1.8.12賦值類型錯(cuò)誤
任何一個(gè)變量,在賦值時(shí)必須使用相同的數(shù)據(jù)類型。例如下面的代碼,錯(cuò)誤地將一個(gè)字
符串賦值給一個(gè)字符。
char c;
c="a";
程序編譯時(shí)的結(jié)果如下所示,表明賦值時(shí)數(shù)據(jù)類型錯(cuò)誤。
5.1.c:19:警告:賦值時(shí)將指針賦給整數(shù),未作類型轉(zhuǎn)換
1.8.13循環(huán)或判斷語句中多加分號(hào)
分號(hào)在程序中的作用是表示一個(gè)語句結(jié)束。在程序的語句中用一個(gè)單獨(dú)的分號(hào)表示一個(gè)
空語句。但是在循環(huán)或判斷結(jié)構(gòu)的后面,一個(gè)分號(hào)會(huì)導(dǎo)致程序的邏輯發(fā)生錯(cuò)誤。關(guān)于這些結(jié)
構(gòu)的使用方法,后面的章節(jié)將會(huì)詳細(xì)講到。下面的程序,在for語句的后面,錯(cuò)誤的添加了一
個(gè)分號(hào),導(dǎo)致程序不能正常地進(jìn)行循環(huán)。
#include<stdio.h>
main()
{
int sum,j;
sum=0;
for(j=0;j<11;j++);
{
sum=sum+j;
}
printf(“%d”,sum);
}
這個(gè)程序的本意是要求出10以內(nèi)的整數(shù)和。但是在for語句的后面,錯(cuò)誤地使用了一個(gè)
分號(hào)。這時(shí),程序不能正確地進(jìn)行循環(huán),而是把分號(hào)作為一個(gè)語句進(jìn)行循環(huán),所以程序輸出
的結(jié)果為“11”。
1.9小結(jié)
程序的編譯和調(diào)試是編程的一個(gè)重要環(huán)節(jié)。本章講解了Linux系統(tǒng)中C編程的編譯器gcc
和編譯器gdb的使用。使用gcc時(shí),需要對(duì)編譯進(jìn)行各種設(shè)置,需要理解gcc各項(xiàng)參數(shù)的作
用。gdb的學(xué)習(xí)重點(diǎn)是gdb單步運(yùn)行程序的理解,通過程序的單步運(yùn)行發(fā)現(xiàn)程序中的問題。
1.1編譯的概念和理解
在進(jìn)行C程序開發(fā)時(shí),編譯就是將編寫的C語言代碼變成可執(zhí)行程序的過程,這一過程
是由編譯器來完成的。編譯器就是完成程序編譯工作的軟件,在進(jìn)行程序編譯時(shí)完成了一系
列復(fù)雜的過程。
1.1.1程序編譯的過程
在執(zhí)行這一操作時(shí),程序完成了復(fù)雜的過程。一個(gè)程序的編譯,需要完成詞法分析、語
法分析、中間代碼生成、代碼優(yōu)化、目標(biāo)代碼生成。本章將講解這些步驟的作用與原理。
(1)詞法分析。指的是對(duì)由字符組成的單詞進(jìn)行處理,從左至右逐個(gè)字符地對(duì)源程序進(jìn)
行掃描,產(chǎn)生一個(gè)個(gè)的單詞符號(hào)。然后把字符串的源程序改造成為單詞符號(hào)串的中間程序。
在編譯程序時(shí),這一過程是自動(dòng)完成的。編譯程序會(huì)對(duì)代碼的每一個(gè)單詞進(jìn)行檢查。如果單
詞發(fā)生錯(cuò)誤,編譯過程就會(huì)停止并顯示錯(cuò)誤。這時(shí)需要對(duì)程序中的錯(cuò)誤進(jìn)行修改。
(2)語法分析。語法分析器以單詞符號(hào)作為輸入,分析單詞符號(hào)串是否形成符合語法規(guī)
則的語句。例如,需要檢查表達(dá)式、賦值、循環(huán)等結(jié)構(gòu)是否完整和符合使用規(guī)則。在語法分
析時(shí),會(huì)分析出程序中錯(cuò)誤的語句,并顯示出結(jié)果。如果語法發(fā)生錯(cuò)誤,編譯任務(wù)是不能完
成的。
(3)中間代碼生成。中間代碼是源程序的一種內(nèi)部表示,或稱中間語言。程序進(jìn)行詞法
分析和語法分析以后,將程序轉(zhuǎn)換成中間代碼。這一轉(zhuǎn)換的作用是使程序的結(jié)構(gòu)更加簡(jiǎn)單和
規(guī)范。中間代碼生成操作是一個(gè)中間過程,與用戶是無關(guān)的。
(4)代碼優(yōu)化。代碼優(yōu)化是指對(duì)程序進(jìn)行多種等價(jià)變換,使得從變換后的程序能生成更
有效的目標(biāo)代碼。用戶可以在編譯程序時(shí)設(shè)置代碼優(yōu)化的參數(shù),可以針對(duì)不同的環(huán)境和設(shè)置
進(jìn)行優(yōu)化。
(5)目標(biāo)代碼生成。目標(biāo)代碼生成指的是產(chǎn)生可以執(zhí)行的應(yīng)用程序,這是編譯的最后一
個(gè)步驟。生成的程序是二進(jìn)制的機(jī)器語言,用戶只能運(yùn)行這個(gè)程序,而不能打開這個(gè)文件查
看程序的代碼。
1.1.2編譯器
所謂編譯器,是將編寫出的程序代碼轉(zhuǎn)換成計(jì)算機(jī)可以運(yùn)行的程序的軟件。在進(jìn)行C程
序開發(fā)時(shí),編寫出的代碼是源程序的代碼,是不能直接運(yùn)行的。需要用編譯器編譯成可以運(yùn)
行的二進(jìn)制程序。
在不同的操作系統(tǒng)下面有不同的編譯器。C程序是可以跨平臺(tái)運(yùn)行的。但并不是說
Windows系統(tǒng)下C語言編寫的程序可以直接在Linux下面運(yùn)行。Windows下面C語言編寫的
程序,被編譯成exe文件。這樣的程序只能在Windows系統(tǒng)下運(yùn)行。如果需要在Linux系統(tǒng)
下運(yùn)行,需要將這個(gè)程序的源代碼在Linux系統(tǒng)重新編譯。不同的操作系統(tǒng)下面有不同的編
譯器。Linux系統(tǒng)下面編譯生成的程序是不能在Windows系統(tǒng)上運(yùn)行的。
1.2 gcc編譯器
gcc是Linux下的C程序編譯器,具有非常強(qiáng)大的程序編譯功能。在Linux系統(tǒng)下,C語
言編寫的程序代碼一般需要通過gcc來編譯成可執(zhí)行程序。
1.2.1 gcc編譯器簡(jiǎn)介
Linux系統(tǒng)下的gcc編譯器(GNU C Compiler)是一個(gè)功能強(qiáng)大、性能優(yōu)越的編譯器。gcc
支持多種平臺(tái)的編譯,是Linux系統(tǒng)自由軟件的代表作品。gcc本來只是C編譯器的,但是后
來發(fā)展為可在多種硬體平臺(tái)上編譯出可執(zhí)行程序的超級(jí)編譯器。各種硬件平臺(tái)對(duì)gcc的支持
使得其執(zhí)行效率與一般的編譯器相比平均效率要高20%~30%。gcc編譯器能將C、C++源程
序、匯程語言和目標(biāo)程序進(jìn)行編譯鏈接成可執(zhí)行文件。通過支持make工具,gcc可以實(shí)施項(xiàng)
目管理和批量編譯。
經(jīng)過多年的發(fā)展,gcc已經(jīng)發(fā)生了很大的變化。gcc已經(jīng)不僅僅能支持C語言,還支持Ada
語言、C++語言、Java語言、Objective C語言、Pascal語言、COBOL語言等更多的語言集的編
譯。gcc幾乎支持所有的硬件平臺(tái),使得gcc對(duì)于特定的平臺(tái)可以編譯出更高效的機(jī)器碼。
gcc在編譯一個(gè)程序時(shí),一般需要完成預(yù)處理(preprocessing)、編譯(compilation)、匯
編(assembly)和鏈接(linking)過程。使用gcc編譯C程序時(shí),這些過程是使用默認(rèn)的設(shè)置
自動(dòng)完成的,但是用戶可以對(duì)這些過程進(jìn)行設(shè)置,控制這些操作的詳細(xì)過程。
1.2.2 gcc對(duì)源程序擴(kuò)展名的支持
擴(kuò)展名指的是文件名中最后一個(gè)點(diǎn)的這個(gè)點(diǎn)以后的部分。例如下面是一個(gè)C程序源文件
的擴(kuò)展名。
5.1.c
那么這個(gè)文件的文件名是“5.1.c”,擴(kuò)展名是“.c”。通常來說,源文件的擴(kuò)展名標(biāo)識(shí)源文
件所使用的編程語言。例如C程序源文件的擴(kuò)展名一般是“.c”。對(duì)編譯器來說,擴(kuò)展名控制
著缺省語言的設(shè)定。在默認(rèn)情況下,gcc通過文件擴(kuò)展名來區(qū)分源文件的語言類型。然后根據(jù)
這種語言類型進(jìn)行不同的編譯。gcc對(duì)源文件的擴(kuò)展名約定如下所示。
.c為擴(kuò)展名的文件,為C語言源代碼文件。
.a為擴(kuò)展名的文件,是由目標(biāo)文件構(gòu)成的庫(kù)文件。
.C,.cc或.cpp為擴(kuò)展名的文件,標(biāo)識(shí)為C++源代碼文件。
.h為擴(kuò)展名的文件,說明文件是程序所包含的頭文件。
.i為擴(kuò)展名的文件,標(biāo)識(shí)文件是已經(jīng)預(yù)處理過的C源代碼文件,一般為中間代碼文件。
.ii為擴(kuò)展名的文件,是已經(jīng)預(yù)處理過的C++源代碼文件,同上也是中間代碼文件。
.o為擴(kuò)展名的文件,是編譯后的目標(biāo)文件,源文件生成的中間目標(biāo)文件。
.s為擴(kuò)展名的文件,是匯編語言源代碼文件。
.S為擴(kuò)展名的文件,是經(jīng)過預(yù)編譯的匯編語言源代碼文件。
.o為擴(kuò)展名的文件,是編譯以后的程序目標(biāo)文件(Object file),目標(biāo)文件經(jīng)過連接成
可執(zhí)行文件
此外,對(duì)于gcc編譯器提供兩種顯示的編譯命令,分別對(duì)應(yīng)于編譯C和C++源程序的
編譯命令。
1.3 C程序的編譯
本章以一個(gè)實(shí)例講述如何用gcc編譯C程序。在編譯程序之前,需要用VIM編寫一個(gè)簡(jiǎn)
單的C程序。在編譯程序時(shí),可以對(duì)gcc命令進(jìn)行不同的設(shè)置。
1.3.1編寫第一個(gè)C程序
本節(jié)將編寫第一個(gè)C程序。程序?qū)崿F(xiàn)一句文本的輸出和判斷兩個(gè)整數(shù)的大小關(guān)系。本書
中編寫程序使用的編輯器是VIM。程序編寫步驟如下所示。
打開系統(tǒng)的終端。單擊“主菜單”|“系統(tǒng)工具”|“終端”命令,打開一個(gè)系統(tǒng)
終端。
在終端中輸入下面的命令,在用戶根目錄“root”中建立一個(gè)目錄。
mkdir c
在終端界面中輸入“vim”命令,然后按“Enter”鍵,系統(tǒng)會(huì)啟動(dòng)VIM。
在VIM中按“i”鍵,進(jìn)入到插入模式。然后在VIM中輸入下面的程序代碼。
#include<stdio.h>
int max(int i,int j)
{
if(i>j)
{
return(i);
}
else
{
return(j);
}
}
void main()
{
int i,j,k;
i=3;
j=5;
printf("hello,Linux./n”);
k=max(i,j);
printf("%d/n",k);
}
代碼輸入完成以后,按“Esc”鍵,返回到普通模式。然后輸入下面的命令,保存文件。
:w/root/c/a.c
這時(shí),VIM會(huì)把輸入的程序保存到c目錄下的文件a.c中。
再輸入“:q”命令,退出VIM。這時(shí),已經(jīng)完成了這個(gè)C程序的編寫。
1.3.2用gcc編譯程序
上面編寫的C程序,只是一個(gè)源代碼文件,還不能作為程序來執(zhí)行。需要用gcc將這個(gè)
源代碼文件編譯成可執(zhí)行文件。編譯文件的步驟如下所示。
打開系統(tǒng)的終端。單擊“主菜單”|“系統(tǒng)工具”|“終端”命令,打開一個(gè)系統(tǒng)
終端。這時(shí)進(jìn)入的目錄是用戶根目錄“/root”。然后輸入下面的命令,進(jìn)入到c目錄。
cd c
上一節(jié)編寫的程序就存放在這個(gè)目錄中。輸入“l(fā)s”命令可以查看這個(gè)目錄下的文件。
顯示的結(jié)果如下所示。
輸入下面的命令,將這個(gè)代碼文件編譯成可執(zhí)行程序。
gcc a.c
查看已經(jīng)編譯的文件。在終端中輸入“l(fā)s”命令,顯示的結(jié)果如下所示。
a.c a.out
輸入下面的命令對(duì)這個(gè)程序添加可執(zhí)行權(quán)限。
chmod+x a.out
輸入下面的命令,運(yùn)行這個(gè)程序。
./a.out
程序的運(yùn)行結(jié)果如下所示。
hello,Linux.
5
從上面的操作可知,用gcc可以將一個(gè)C程序源文件編譯成一個(gè)可執(zhí)行程序。編譯以
后的程序需要添加可執(zhí)行的權(quán)限才可以運(yùn)行。在實(shí)際操作中,還需要對(duì)程序的編譯進(jìn)行各
種設(shè)置。
1.3.3查看gcc的參數(shù)
gcc在編譯程序時(shí)可以有很多可選參數(shù)。在終端中輸入下面的命令,可以查看gcc的這些
可選參數(shù)。
gcc--help
在終端中顯示的gcc的可選參數(shù)如下所示。進(jìn)行程序編譯時(shí),可以設(shè)置下面的這些參數(shù)。
用法:gcc[選項(xiàng)]文件...
選項(xiàng):
-pass-exit-codes:在某一階段退出時(shí)返回最高的錯(cuò)誤碼
--help:顯示此幫助說明
--target-help:顯示目標(biāo)機(jī)器特定的命令行選項(xiàng)
-dumpspecs:顯示所有內(nèi)建spec字符串
-dumpversion:顯示編譯器的版本號(hào)
-dumpmachine:顯示編譯器的目標(biāo)處理器
-print-search-dirs:顯示編譯器的搜索路徑
-print-libgcc-file-name:顯示編譯器伴隨庫(kù)的名稱
-print-file-name=<庫(kù)>:顯示<庫(kù)>的完整路徑
-print-prog-name=<程序>:顯示編譯器組件<程序>的完整路徑
-print-multi-directory:顯示不同版本libgcc的根目錄
-print-multi-lib:顯示命令行選項(xiàng)和多個(gè)版本庫(kù)搜索路徑間的映射
-print-multi-os-directory:顯示操作系統(tǒng)庫(kù)的相對(duì)路徑
-Wa,<選項(xiàng)>:將逗號(hào)分隔的<選項(xiàng)>傳遞給匯編器
-Wp,<選項(xiàng)>:將逗號(hào)分隔的<選項(xiàng)>傳遞給預(yù)處理器
-Wl,<選項(xiàng)>:將逗號(hào)分隔的<選項(xiàng)>傳遞給鏈接器
-Xassembler<參數(shù)>:將<參數(shù)>傳遞給匯編器
-Xpreprocessor<參數(shù)>:將<參數(shù)>傳遞給預(yù)處理器
-Xlinker<參數(shù)>:將<參數(shù)>傳遞給鏈接器
-combine:將多個(gè)源文件一次性傳遞給匯編器
-save-temps:不刪除中間文件
-pipe:使用管道代替臨時(shí)文件
-time:為每個(gè)子進(jìn)程計(jì)時(shí)
-specs=<文件>:用<文件>的內(nèi)容覆蓋內(nèi)建的specs文件
-std=<標(biāo)準(zhǔn)>:指定輸入源文件遵循的標(biāo)準(zhǔn)
--sysroot=<目錄>:將<目錄>作為頭文件和庫(kù)文件的根目錄
-B<目錄>:將<目錄>添加到編譯器的搜索路徑中
-b<機(jī)器>:為gcc指定目標(biāo)機(jī)器(如果有安裝)
-V<版本>:運(yùn)行指定版本的gcc(如果有安裝)
-v:顯示編譯器調(diào)用的程序
-###:與-v類似,但選項(xiàng)被引號(hào)括住,并且不執(zhí)行命令
-E:僅作預(yù)處理,不進(jìn)行編譯、匯編和鏈接
-S:編譯到匯編語言,不進(jìn)行匯編和鏈接
-c:編譯、匯編到目標(biāo)代碼,不進(jìn)行鏈接
-o<文件>:輸出到<文件>
-x<語言>:指定其后輸入文件的語言。允許的語言包括c、c++、assembler等。
以-g、-f、-m、-O、-W或--param開頭的選項(xiàng)將由gcc自動(dòng)傳遞給其調(diào)用的不同子進(jìn)程。若要
向這些進(jìn)程傳遞其他選項(xiàng),必須使用-W<字母>選項(xiàng)。
1.3.4設(shè)置輸出的文件
在默認(rèn)情況下,gcc編譯出的程序?yàn)楫?dāng)前目錄下的文件a.out。-o參數(shù)可以設(shè)置輸出的目
標(biāo)文件。例如下面的命令,可以設(shè)置將代碼編譯成可執(zhí)行程序do。
gcc a.c-o do
也可以設(shè)置輸出目錄文件為不同的目錄。例如下面的命令,是將目錄文件設(shè)置成/tmp目
錄下的文件do。
gcc a.c-o/tmp/do
輸入下面的命令,查看生成的目錄文件。結(jié)果如下所示,在編譯程序時(shí)生成的目錄為/tmp
目錄下的文件do。
-rwxrwxr-x 1 root root 5109 12-28 13:33/tmp/do
1.3.5查看編譯過程
參數(shù)-v可以查看程序的編譯過程和顯示已經(jīng)調(diào)用的庫(kù)。輸入下面的命令,在編譯程序時(shí)
輸出編譯過程。
gcc-v a.c
顯示的結(jié)果如下所示。
使用內(nèi)建specs。
目標(biāo):i386-redhat-linux
配置為:../configure--prefix=/usr--mandir=/usr/share/man
--infodir=/usr/share/info--enable-shared--enable-threads=posix
--enable-checking=release--with-system-zlib--enable-__cxa_atexit
--disable-libunwind-exceptions
--enable-languages=c,c++,objc,obj-c++,java,fortran,ada
--enable-java-awt=gtk--disable-dssi--enable-plugin
--host=i386-redhat-linux
線程模型:posix
gcc版本4.1.2 20070925(Red Hat 4.1.2-33)
/usr/libexec/gcc/i386-redhat-linux/4.1.2/cc1
-quiet-v a.c-quiet-dumpbase a.c-mtune=generic-auxbase a-version
-o/tmp/cc8P7rzb.s
忽略不存在的目錄“/usr/lib/gcc/i386-redhat-linux/4.1.2/../../../../i386-
redhat-linux/include”
#include"..."搜索從這里開始:
#include<...>搜索從這里開始:
/usr/local/include
/usr/lib/gcc/i386-redhat-linux/4.1.2/include
/usr/include
搜索列表結(jié)束。
GNU C版本4.1.2 20070925(Red Hat 4.1.2-33)(i386-redhat-linux)
由GNU C版本4.1.2 20070925(Red Hat 4.1.2-33)編譯。
GGC準(zhǔn)則:--param ggc-min-expand=64--param ggc-min-heapsize=64394
Compiler executable checksum:ab322ce5b87a7c6c23d60970ec7b7b31
a.c:In function‘main’:
a.c:16:警告:‘main’的返回類型不是‘int’
as-V-Qy-o/tmp/ccEFPrYh.o/tmp/cc8P7rzb.s
GNU assembler version 2.17.50.0.18(i386-redhat-linux)using BFD version
version 2.17.50.0.18-1 20070731
/usr/libexec/gcc/i386-redhat-linux/4.1.2/collect2
--eh-frame-hdr--build-id-m elf_i386--hash-style=gnu-dynamic-linker
/lib/ld-linux.so.2/usr/lib/gcc/i386-redhat-linux/4.1.2/../../../crt1.o
從顯示的編譯過程可知,gcc自動(dòng)加載了系統(tǒng)的默認(rèn)配置,調(diào)用系統(tǒng)的庫(kù)函數(shù)完成了程序
的編譯過程。
1.3.6設(shè)置編譯的語言
gcc可以對(duì)多種語言編寫的源代碼。如果源代碼的文件擴(kuò)展名不是默認(rèn)的擴(kuò)展名,gcc就
無法編譯這個(gè)程序。可以用-x選擇來設(shè)置程序的語言。可以用下面的步驟來練習(xí)這一操作。
輸入下面的命令,將C程序文件復(fù)制一份。
cp a.c a.u
復(fù)制出的文件a.u是一個(gè)C程序文件,但擴(kuò)展名不是默認(rèn)的擴(kuò)展名。這時(shí)輸入下面的
命令編譯這個(gè)程序。
gcc a.u
顯示的結(jié)果如下所示,表明文件的格式不能識(shí)別。
a.u:file not recognized:File format not recognized
collect2:ld返回1
這時(shí),用-x參數(shù)設(shè)置編譯的語言,命令如下所示。這樣就可以正常地編譯文件a.u。
gcc-x'c'a.u
需要注意的是,這里的c需要用單引號(hào)擴(kuò)起來。當(dāng)編譯擴(kuò)展名不是.c的C程序時(shí),需要
使用-x參數(shù)。
1.3.7-asci設(shè)置ANSIC標(biāo)準(zhǔn)
ANSIC是American National Standards Institute(ANSI:美國(guó)標(biāo)準(zhǔn)協(xié)會(huì))出版的C語言
標(biāo)準(zhǔn)。使用這種標(biāo)準(zhǔn)的C程序可以在各種編譯器和系統(tǒng)下運(yùn)行通過。gcc可以編譯ANSIC的
程序,但是gcc中的很多標(biāo)準(zhǔn)并不被ANSIC所支持。在gcc編譯程序時(shí),可以用-ansic來設(shè)置
程序使用ANSIC標(biāo)準(zhǔn)。例如下面的命令,在設(shè)置程序編譯時(shí),用ANSIC標(biāo)準(zhǔn)進(jìn)行編譯。
gcc-asci a.out
5.3.8
1.3.8 g++編譯C++程序
gcc可以編譯C++程序。編譯C程序和C++程序時(shí),使用的是不同的命令。編譯C++程
序時(shí),使用的命令是g++。該命令的使用方法與gcc是相似的。下面是使用g++命令編譯C++
程序的實(shí)例。
下面是一個(gè)C++程序的代碼,實(shí)現(xiàn)與1.3.1節(jié)程序同樣的功能。C++程序的代碼與C程序
的代碼非常相似。
#include<iostream>
int max(int i,int j)
{
if(i>j)
{
return(i);
}
else
{
return(j);
}
}
int main()
{
int i,j,k;
i=3;
j=5;
printf("hello,Linux./n");
k=max(i,j);
printf("%d/n",k);
return(0);
}
輸入下面的命令,編譯這個(gè)C++程序。
g++5.2.cpp–o 5.2.out
輸入下面的命令,對(duì)這個(gè)程序添加可執(zhí)行權(quán)限。
chmod+x 5.2.ou
輸入下面的命令,運(yùn)行這個(gè)程序,程序的代碼如下所示。
hello,Linux.
5
從結(jié)果可知,這個(gè)程序與1.3.1節(jié)中的程序運(yùn)行結(jié)果是相同的。
1.4編譯過程的控制
編譯過程指的是gcc對(duì)一個(gè)程序進(jìn)行編譯時(shí)完成的內(nèi)部處理和步驟。編譯程序時(shí)會(huì)自動(dòng)
完成預(yù)處理(Preprocessing)、編譯(Compilation)、匯編(Assembly)和鏈接(Linking)4個(gè)
步驟。本節(jié)將講解如何對(duì)這4個(gè)步驟進(jìn)行控制。
1.4.1編譯過程簡(jiǎn)介
gcc把一個(gè)程序的源文件,編譯成一個(gè)可執(zhí)行文件,中間包括很多復(fù)雜的過程。可以用圖
1-1來表示編譯中4個(gè)步驟的作用和關(guān)系。
源文件
匯編文件
目標(biāo)文件
可執(zhí)行文件
使用-E選項(xiàng)進(jìn)行預(yù)處理
使用-S選項(xiàng)匯編
-c選項(xiàng)生成目標(biāo)文件
鏈接目標(biāo)文件和庫(kù)
生成可執(zhí)行文件
預(yù)處理文件
圖1-1 gcc編譯源文件到可執(zhí)行文件的過程
在4個(gè)過程中,每一個(gè)操作都完成了不同的功能。編譯過程的功能如下所示。
預(yù)處理:在預(yù)處理階段,主要完成對(duì)源代碼中的預(yù)編譯語句(如宏定義define等)和
文件包含進(jìn)行處理。需要完成的工作是對(duì)預(yù)編譯指令進(jìn)行替換,把包含文件放置到需
要編譯的文件中。完成這些工作后,會(huì)生成一個(gè)非常完整的C程序源文件。
編譯:gcc對(duì)預(yù)處理以后的文件進(jìn)行編譯,生成以.s為后綴的匯編語言文件。該匯編語
言文件是編譯源代碼得到的匯編語言代碼,接下來交給匯編過程進(jìn)行處理。匯編語言
是一種比C語言更低級(jí)的語言,直接面對(duì)硬盤進(jìn)行操作。程序需要編譯成匯編指令以
后再編譯成機(jī)器代碼。
匯編:匯編過程是處理匯編語言的階段,主要調(diào)用匯編處理程序完成將匯編語言匯編
成二進(jìn)制機(jī)器代碼的過程。通常來說,匯編過程是將.s的匯編語言代碼文件匯編為.o
的目標(biāo)文件的過程。所生成的目標(biāo)文件作為下一步鏈接過程的輸入文件。
鏈接:鏈接過程就是將多個(gè)匯編生成的目標(biāo)文件以及引用的庫(kù)文件進(jìn)行模塊鏈接生成
一個(gè)完整的可執(zhí)行文件。在鏈接階段,所有的目標(biāo)文件被安排在可執(zhí)行程序中的適當(dāng)
的位置。同時(shí),該程序所調(diào)用到的庫(kù)函數(shù)也從各自所在的函數(shù)庫(kù)中鏈接到程序中。經(jīng)
過了這個(gè)過程以后,生成的文件就是可執(zhí)行的程序。
1.4.2控制預(yù)處理過程
參數(shù)-E可以完成程序的預(yù)處理工作而不進(jìn)行其他的編譯工作。下面的命令,可以將本章
編寫的程序進(jìn)行預(yù)處理,然后保存到文件a.cxx中。
gcc-E-o a.cxx a.c
輸入下面的命令,查看經(jīng)過預(yù)處理以后的a.cxx文件。
vim a.cxx
可以發(fā)現(xiàn),文件a.cxx約有800行代碼。程序中默認(rèn)包含的頭文件已經(jīng)被展開寫到到這個(gè)
預(yù)處理文件
匯編文件
目標(biāo)文件
可執(zhí)行文件
鏈接目標(biāo)文件和庫(kù)
生成可執(zhí)行文件
-c選項(xiàng)生成目標(biāo)文件
使用-s選項(xiàng)匯編
使用-E選項(xiàng)進(jìn)行預(yù)處理
文件中。顯示的文件a.cxx前幾行代碼如下所示。可見,在程序編譯時(shí),需要調(diào)用非常多的頭
文件和系統(tǒng)庫(kù)函數(shù)。
#1"a.c"
#1"<built-in>"
#1"<command line>"
#1"a.c"
#1"/usr/include/stdio.h"1 3 4
#28"/usr/include/stdio.h"3 4
#1"/usr/include/features.h"1 3 4
#335"/usr/include/features.h"3 4
#1"/usr/include/sys/cdefs.h"1 3 4
#360"/usr/include/sys/cdefs.h"3 4
#1"/usr/include/bits/wordsize.h"1 3 4
#361"/usr/include/sys/cdefs.h"2 3 4
#336"/usr/include/features.h"2 3 4
#359"/usr/include/features.h"3 4
#1"/usr/include/gnu/stubs.h"1 3 4
#1"/usr/include/bits/wordsize.h"1 3 4
#5"/usr/include/gnu/stubs.h"2 3 4
#1"/usr/include/gnu/stubs-32.h"1 3 4
#8"/usr/include/gnu/stubs.h"2 3 4
#360"/usr/include/features.h"2 3 4
#29"/usr/include/stdio.h"2 3 4
1.4.3生成匯編代碼
參數(shù)-S可以控制gcc在編譯C程序時(shí)只生成相應(yīng)的匯編程序文件,而不繼續(xù)執(zhí)行后面的
編譯。下面的命令,可以將本章中的C程序編譯成一個(gè)匯編程序。
gcc-S-o a.s a.c
輸入下面的命令,查看匯編文件a.s。可以發(fā)現(xiàn)這個(gè)文件一共有60行代碼。這些代碼是
這個(gè)程序的匯編指令。部分匯編程序代碼如下所示。
.file"a.c"
.text
.globl max
.type max,@function
max:
pushl%ebp
movl%esp,%ebp
subl$4,%esp
movl 8(%ebp),%eax
cmpl 12(%ebp),%eax
jle.L2
movl 8(%ebp),%eax
movl%eax,-4(%ebp)
jmp.L4
.L2:
movl 12(%ebp),%eax
movl%eax,-4(%ebp)
.L4:
movl-4(%ebp),%eax
leave
ret
.size max,.-max
.section.rodata
.LC0:
.string"hello,Linux."
.LC1:
.string"%d/n"
.text
.globl main
.type main,@function
main:
leal 4(%esp),%ecx
andl$-16,%esp
.......
popl%ebp
leal-4(%ecx),%esp
ret
.size main,.-main
.ident"GCC:(GNU)4.1.2 20070925(Red Hat 4.1.2-33)"
.section.note.GNU-stack,"",@progbits
1.4.4生成目標(biāo)代碼
參數(shù)-c可以使得gcc在編譯程序時(shí)只生成目錄代碼而不生成可執(zhí)行程序。輸入下面的命
令,將本章中的程序編譯成目錄代碼。
gcc-c-o a.o a.c
輸入下面的命令,查看這個(gè)目錄代碼的信息。
file a.o
顯示文件a.o的結(jié)果如下所示,顯示文件a.o是一個(gè)可重定位的目標(biāo)代碼文件。
a.o:ELF 32-bit LSB relocatable,Intel 80386,version 1(SYSV),not stripped
1.4.5鏈接生成可執(zhí)行文件
gcc可以把上一步驟生成的目錄代碼文件生成一個(gè)可執(zhí)行文件。在終端中輸入下面的命令。
gcc a.o-o aa.out
這時(shí)生成一個(gè)可執(zhí)行文件aa.out。輸入下面的命令查看這個(gè)文件的信息。
file aa.out
顯示的結(jié)果如下所示,表明這個(gè)文件是可在Linux系統(tǒng)下運(yùn)行的程序文件。
aa.out:ELF 32-bit LSB executable,Intel 80386,version 1(SYSV),dynamically
linked(uses shared libs),for GNU/Linux 2.6.9,not stripped
1.5 gdb調(diào)試程序
所謂調(diào)試,指的是對(duì)編好的程序用各種手段進(jìn)行查錯(cuò)和排錯(cuò)的過程。進(jìn)行這種查錯(cuò)處理
時(shí),并不僅僅是運(yùn)行一次程序檢查結(jié)果,而是對(duì)程序的運(yùn)行過程、程序中的變量進(jìn)行各種分
析和處理。本節(jié)將講解使用gdb進(jìn)行程序的調(diào)試。
1.5.1 gdb簡(jiǎn)介
gdb是一個(gè)功能強(qiáng)大的調(diào)試工具,可以用來調(diào)試C程序或C++程序。在使用這個(gè)工具進(jìn)
行程序調(diào)試時(shí),主要使用gdb進(jìn)行下面5個(gè)方面的操作。
啟動(dòng)程序:在啟動(dòng)程序時(shí),可以設(shè)置程序運(yùn)行環(huán)境。
設(shè)置斷點(diǎn):斷點(diǎn)就是可以在程序設(shè)計(jì)時(shí)暫停程序運(yùn)行的標(biāo)記。程序會(huì)在斷點(diǎn)處停止,
用戶便于查看程序的運(yùn)行情況。這里的斷點(diǎn)可以是行數(shù)、程序名稱或條件表達(dá)式。
查看信息:在斷點(diǎn)停止后,可以查看程序的運(yùn)行信息和顯示程序變量的值。
分步運(yùn)行:可以使程序一個(gè)語句一個(gè)語句的執(zhí)行,這時(shí)可以及時(shí)地查看程序的信息。
改變環(huán)境:可以在程序運(yùn)行時(shí)改變程序的運(yùn)行環(huán)境和程序變量。
1.5.2在程序中加入調(diào)試信息
為了使用gdb進(jìn)行程序調(diào)試,需要在編譯程序中加入供gdb使用的調(diào)試信息。方法是在
編譯程序時(shí)使用一個(gè)-g參數(shù)。在終端中輸入下面的命令,在編譯程序時(shí)加入調(diào)試信息。
gcc-g-o a.debug a.c
這時(shí),編譯程序a.c,生成一個(gè)a.bedug的可執(zhí)行程序。這個(gè)可執(zhí)行程序中加入了供調(diào)試
所用的信息。
1.5.3啟動(dòng)gdb
在調(diào)試文件以前,需要啟動(dòng)gdb。在終端中輸入下面的命令。
gdb
這時(shí),gdb的啟動(dòng)信息如下所示。這些提示顯示了gdb的版本和版權(quán)信息。
GNU gdb Red Hat Linux(6.6-35.fc8rh)
Copyright(C)2006 Free Software Foundation,Inc.
GDB is free software,covered by the GNU General Public License,and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type"show copying"to see the conditions.
There is absolutely no warranty for GDB.Type"show warranty"for details.
This GDB was configured as"i386-redhat-linux-gnu".
(gdb)
1.5.4在gdb中加載需要調(diào)試的程序
使用gdb調(diào)試一個(gè)程序之前,需要加載這個(gè)程序。加載程序的命令是file。在(gdb)提示
符后面輸入下面的命令加載程序a.debug。
file a.debug
命令的運(yùn)行結(jié)果如下所示,顯示已經(jīng)加載了這個(gè)文件,并且使用了系統(tǒng)庫(kù)文件。
Reading symbols from/root/c/a.debug...done.
Using host libthread_db library"/lib/libthread_db.so.1".
1.5.5在gdb中查看代碼
用gcc命令編譯程序加入了-g命令以后,編譯后的a.debug程序中加入了斷點(diǎn)。可以用list
命令顯示程序的源代碼和斷點(diǎn)。下面的步驟是查看加入斷點(diǎn)以后的代碼。
在(gdb)提示符后面輸入下面的命令。
list 1
這時(shí),gdb會(huì)顯示第一個(gè)斷點(diǎn)以前的代碼。顯示的代碼如下所示。
1#include<stdio.h>
2
3 int max(int i,int j)
4{
5 if(i>j)
6{
7 return(i);
8}
9 else
10{
(gdb)
這時(shí),按“Enter”鍵,顯示下一個(gè)斷點(diǎn)以前的代碼。結(jié)果如下所示。
11 return(j);
12}
13}
14
15 void main()
16{
17 int i,j,k;
18 i=3;
19 j=5;
20 printf("hello,Linux./n");
(gdb)
按“Enter”鍵,顯示下一個(gè)斷點(diǎn)以前的代碼。結(jié)果如下所示。
21 k=max(i,j);
22 printf("%d/n",k);
23}
(gdb)
1.5.6在程序中加入斷點(diǎn)
程序會(huì)運(yùn)行到斷點(diǎn)的位置停止下來,等待用戶處理信息或者查看中間變量。如果自動(dòng)設(shè)
置的斷點(diǎn)不能滿足調(diào)試要求,可以用break命令增加程序的斷點(diǎn)。例如需要在程序的第6行增
加一個(gè)斷點(diǎn),可以輸入下面的命令。
break 6
這時(shí)gdb顯示的結(jié)果如下所示。
Breakpoint 1 at 0x8048402:file a.c,line 6.
輸入下面的命令,在程序的第18行、19行、21行增加斷點(diǎn)。
break 18
break 19
break 21
1.5.7查看斷點(diǎn)
命令info breakpoint可以查看程序中設(shè)置的斷點(diǎn)。輸入“info breakpoint”命令,結(jié)果如
下所示。顯示程序中所有的斷點(diǎn)。
1 breakpoint keep y 0x08048402 in max at a.c:6
2 breakpoint keep y 0x08048426 in main at a.c:18
3 breakpoint keep y 0x0804842d in main at a.c:19
4 breakpoint keep y 0x08048440 in main at a.c:21
加上相應(yīng)的斷點(diǎn)編號(hào),可以查看這一個(gè)斷點(diǎn)的信息。例如下面的命令就是查看第二個(gè)斷點(diǎn)。
info breakpoint 2
顯示的結(jié)果如下所示。
2 breakpoint keep y 0x08048426 in main at a.c:18
1.5.8運(yùn)行程序
gdb中的run命令可以使這個(gè)程序以調(diào)試的模式運(yùn)行。下面的步驟是分步運(yùn)行程序,對(duì)程
序進(jìn)行調(diào)試。
在(ddb)提示符后輸入“run”命令,顯示的結(jié)果如下所示。
Starting program:/root/c/a.debug
warning:Missing the separate debug info file:
/usr/lib/debug/.build-id/ac/2eeb206486bb7315d6ac4cd64de0cb50838ff6.debug
warning:Missing the separate debug info file:
/usr/lib/debug/.build-id/ba/4ea1118691c826426e9410cafb798f25cefad5.debug
Breakpoint 2,main()at a.c:18
18 i=3;
結(jié)果顯示了程序中的異常,并將異常記錄到了系統(tǒng)文件中。然后在程序的第二個(gè)斷
點(diǎn)的位置第18行停下。
這時(shí)輸入“next”命令,程序會(huì)在下一行停下,結(jié)果如下所示。
19 j=5;
輸入“continue”命令,程序會(huì)在下一個(gè)斷點(diǎn)的位置停下。結(jié)果如下所示。
Continuing.
Breakpoint 3,main()at a.c:19
21 k=max(i,j);
輸入“continue”命令,程序運(yùn)行到結(jié)束。結(jié)果如下所示,表明程序已經(jīng)運(yùn)行完畢正
常退出。
5
Program exited with code 02.
step命令與next命令的作用相似,對(duì)程序?qū)崿F(xiàn)單步運(yùn)行。不同之處是,在遇上函數(shù)
調(diào)用時(shí),step函數(shù)可以進(jìn)行到函數(shù)內(nèi)部。而next函數(shù)只是一步完成函數(shù)的調(diào)用。
1.5.9變量的查看
print命令可以在程序的運(yùn)行中查看一個(gè)變量的值。本節(jié)將用下面的步驟來講解變量的查
看方法。
輸入下面的命令,運(yùn)行程序。
run
程序在第一個(gè)斷點(diǎn)位置停下。顯示的結(jié)果如下所示。
Breakpoint 2,main()at a.c:18
18 i=3;
程序進(jìn)入第18行之前停下,并沒有對(duì)i進(jìn)行賦值。可以用下面的命令來查看i的值。
print i
顯示的結(jié)果如下所示,表示i現(xiàn)在只是一個(gè)任意值。
$5=-1076190040
輸入下面的命令,使程序運(yùn)行一步。
step
顯示的結(jié)果如下所示。
19 j=5;
這時(shí)程序在19行以前停下,這時(shí)輸入下面的命令,查看i的值。
print i
這時(shí)顯示的i的結(jié)果如下所示。表明i已經(jīng)賦值為3。
$6=3
這時(shí)輸入“step”命令,再次輸入“step”命令,顯示的結(jié)果如下所示。
21 k=max(i,j);
這時(shí)輸入“step”命令,會(huì)進(jìn)入到子函數(shù)中,結(jié)果如下所示。這時(shí),顯示了傳遞給函
數(shù)的變量和值。
max(i=3,j=5)at a.c:5
5 if(i>j)
這時(shí),輸入“step”命令,顯示的結(jié)果如下所示,表明函數(shù)會(huì)返回變量j。
11 return(j);
輸入下面的命令,查看j的值。
print j
顯示的結(jié)果如下所示,表明j的值為5。
$7=5
這時(shí)再運(yùn)行兩次“step”命令,顯示的結(jié)果如下所示。
22 printf("%d/n",k);
這時(shí),輸入下面的命令,查看k的值。
print k
顯示的結(jié)果如下所示,表明k的值為5。
$8=5
17完成了程序的調(diào)試運(yùn)行以后,輸入“q”命令,退出gdb。
1.6程序調(diào)試實(shí)例
本節(jié)講解一個(gè)程序調(diào)試實(shí)例。先編寫一個(gè)程序,在程序運(yùn)行時(shí),發(fā)現(xiàn)結(jié)果與預(yù)想結(jié)果有
些不同。然后用gdb工具進(jìn)行調(diào)試,通過對(duì)單步運(yùn)行和變量的查看,查找出程序的錯(cuò)誤。
1.6.1編寫一個(gè)程序
本節(jié)將編寫一個(gè)程序,要求程序運(yùn)行時(shí)可以顯示下面的結(jié)果。
1+1=2
2+1=3 2+2=4
3+1=4 3+2=5 3+3=6
4+1=5 4+2=6 4+3=7 4+4=8
很明顯,這個(gè)程序是通過兩次循環(huán)與一次判斷得到的。程序中需要定義三個(gè)變量。下面
用這個(gè)思路來編寫這個(gè)程序。
打開一個(gè)終端。在終端中輸入“vim”命令,打開VIM。
在VIM中按“i”鍵,進(jìn)入到插入模式。然后在VIM中輸入下面的代碼。
#include<stdio.h>
main()
{
int i,j,k;
for(i=1;i<=4;i++)
{
for(j=1;j<=4;j++);
{
if(i>=j)
{
k=i+j;
printf("%d+%d=%d",i,j,k);
}
}
printf("/n");
}
}
在VIM中按“Esc”鍵,返回到普通模式。然后輸入下面的命令,保存這個(gè)文件。
:w/root/c/test.c
輸入“:q”命令退出VIM。很容易發(fā)現(xiàn),在第二個(gè)循環(huán)后
1.6.2編譯文件
本節(jié)將對(duì)上一節(jié)編寫的程序進(jìn)行編譯和運(yùn)行。在運(yùn)行程序時(shí),會(huì)發(fā)現(xiàn)程序有錯(cuò)誤。
在終端中輸入下面的命令,編譯這個(gè)程序。
gcc/root/c/test.c
程序可以正常編譯通過,輸入下面的命令,運(yùn)行這個(gè)程序。
/root/c/a.out
程序的顯示結(jié)果是4個(gè)空行,并沒有按照預(yù)想的要求輸出結(jié)果。
輸入下面的命令,對(duì)這個(gè)程序進(jìn)行編譯。在編譯加入-g參數(shù),為gdb調(diào)試做準(zhǔn)備。
gcc-g-o test.debug 6.2.c
這時(shí),程序可以正常編譯通過。輸出的文件是test.debug。這個(gè)文件中加入了文件調(diào)
試需要的信息。
1.6.3程序的調(diào)試
本節(jié)將講述使用gdb對(duì)上一節(jié)編寫的程序進(jìn)行調(diào)試,查找出程序中的錯(cuò)誤。
在終端中輸入“gdb”命令,進(jìn)入到gdb,顯示的結(jié)果如下所示。
GNU gdb Red Hat Linux(6.6-35.fc8rh)
Copyright(C)2006 Free Software Foundation,Inc.
GDB is free software,covered by the GNU General Public License,and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type"show copying"to see the conditions.
There is absolutely no warranty for GDB.Type"show warranty"for details.
This GDB was configured as"i386-redhat-linux-gnu".
導(dǎo)入文件。在gdb中輸入下面的命令。
file/root/c/test.debug
這時(shí)顯示的結(jié)果如下所示。表明已經(jīng)成功加載了這個(gè)文件。
Reading symbols from/root/c/test.debug...(no debugging symbols
found)...done.
Using host libthread_db library"/lib/libthread_db.so.1".
查看文件。在終端中輸入下面的命令。
list
顯示的文件查看結(jié)果如下所示。
1#include<stdio.h>
2
3 main()
4{
5 int i,j,k;
6 for(i=1;i<=4;i++)
7{
8 for(j=1;j<=4;j++);
9{
10 if(i>=j)
(gdb)
11{
12 k=i+j;
13 printf("%d+%d=%d",i,j,k);
14}
15}
16 printf("/n");
17}
18}
(gdb)
Line number 19 out of range;6.2.c has 18 lines.
在程序中加入斷點(diǎn)。從顯示的代碼可知,需要在第6行、第11行、第12行和第13
行加入斷點(diǎn)。在gdb中輸入下面的命令。
break 6
break 11
break 12
break 13
gdb顯示的添加斷點(diǎn)的結(jié)果如下所示。
Breakpoint 1 at 0x8048405:file 6.2.c,line 6.
Breakpoint 2 at 0x8048429:file 6.2.c,line 11.
Breakpoint 3 at 0x8048429:file 6.2.c,line 12.
Breakpoint 4 at 0x8048432:file 6.2.c,line 13.
輸入下面的命令,運(yùn)行這個(gè)程序。
run
運(yùn)行到第一個(gè)斷點(diǎn)顯示的結(jié)果如下所示。
Breakpoint 1,main()at 6.2.c:6
6 for(i=1;i<=4;i++)
輸入“step”命令,程序運(yùn)行一步,結(jié)果如下所示。
8 for(j=1;j<=4;j++);
這說明程序已經(jīng)進(jìn)入了for循環(huán)。這時(shí)輸入下面命令,查看i的值。
print i
顯示的結(jié)果如下所示。
$2=1
這時(shí)再輸入“step”命令,顯示的結(jié)果如下所示。
10 if(i>=j)
這時(shí)再輸入“step”命令,顯示的結(jié)果如下所示。
16 printf("/n");
這表明,在進(jìn)行j的for循環(huán)時(shí),沒有反復(fù)執(zhí)行循環(huán)體。這時(shí)再輸入“step”命令,
顯示的結(jié)果如下所示。
for(i=1;i<=4;i++)
這表明,程序正常的進(jìn)行了i的for循環(huán)。這是第二次執(zhí)行for循環(huán)。
17輸入“step”命令,顯示的結(jié)果如下所示。
8 for(j=1;j<=4;j++);
18這表明,程序執(zhí)行到for循環(huán)。這時(shí)再次輸入“step”命令,顯示的結(jié)果如下所示。
10 if(i>=j)
19輸入“step”命令,顯示的結(jié)果如下所示。
16 printf("/n");
20輸入“step”命令,顯示的結(jié)果如下所示。
6 for(i=1;i<=4;i++)
21這說明,程序正常的進(jìn)行了i的for循環(huán),但是沒有執(zhí)行j的for循環(huán)。這一定是j的
for循環(huán)語句有問題。這時(shí)就不難發(fā)現(xiàn)j的for循環(huán)后面多了一個(gè)分號(hào)。
22輸入“q”命令,退出gdb。
1.6.4 gdb幫助的使用
gdb有非常多的命令。輸入“help”命令可以顯示這些命令的幫助信息。本節(jié)將講解幫助
信息的使用。
在gdb輸入“help”命令,顯示的幫助信息如下所示。
List of classes of commands:
aliases--Aliases of other commands
breakpoints--Making program stop at certain points
data--Examining data
files--Specifying and examining files
internals--Maintenance commands
obscure--Obscure features
running--Running the program
stack--Examining the stack
status--Status inquiries
support--Support facilities
tracepoints--Tracing of program execution without stopping the program
user-defined--User-defined commands
Type"help"followed by a class name for a list of commands in that class.
Type"help all"for the list of all commands.
Type"help"followed by command name for full documentation.
Type"apropos word"to search for commands related to"word".
Command name abbreviations are allowed if unambiguous.
上面的幫助信息顯示,輸入“help all”會(huì)輸出所有幫助信息。
在“help”命令后面加上一個(gè)命令名稱,可以顯示這個(gè)命令的幫助信息。例如輸入
“help file”,顯示的file命令幫助信息如下所示。
Use FILE as program to be debugged.
It is read for its symbols,for getting the contents of pure memory,
and it is the program executed when you use the`run'command.
If FILE cannot be found as specified,your execution directory path
($PATH)is searched for a command of that name.
No arg means to have no executable file and no symbols.
1.7 gdb常用命令
除了前面講述的gdb命令以外,gdb還有很多種命令。這些命令可以完成程序調(diào)試的各
種功能。其他的常用命令含義如下所示。
backtrace:顯示程序中的當(dāng)前位置和表示如何到達(dá)當(dāng)前位置的棧跟蹤(同義詞:where)。
breakpoint:在程序中設(shè)置一個(gè)斷點(diǎn)。
cd:改變當(dāng)前工作目錄。
clear:刪除剛才停止處的斷點(diǎn)。
commands:命中斷點(diǎn)時(shí),列出將要執(zhí)行的命令。
continue:從斷點(diǎn)開始繼續(xù)執(zhí)行。
delete:刪除一個(gè)斷點(diǎn)或監(jiān)測(cè)點(diǎn),也可與其他命令一起使用。
display:程序停止時(shí)顯示變量和表達(dá)式。
down:下移棧幀,使得另一個(gè)函數(shù)成為當(dāng)前函數(shù)。
frame:選擇下一條continue命令的幀。
info:顯示與該程序有關(guān)的各種信息。
info break:顯示當(dāng)前斷點(diǎn)清單,包括到達(dá)斷點(diǎn)處的次數(shù)等。
info files:顯示被調(diào)試文件的詳細(xì)信息。
info func:顯示所有的函數(shù)名稱。
info local:顯示當(dāng)函數(shù)中的局部變量信息。
info prog:顯示被調(diào)試程序的執(zhí)行狀態(tài)。
info var:顯示所有的全局和靜態(tài)變量名稱。
jump:在源程序中的另一點(diǎn)開始運(yùn)行。
kill:異常終止在gdb控制下運(yùn)行的程序。
list:列出相應(yīng)于正在執(zhí)行的程序的源文件內(nèi)容。
next:執(zhí)行下一個(gè)源程序行,從而執(zhí)行其整體中的一個(gè)函數(shù)。
print:顯示變量或表達(dá)式的值。
pwd:顯示當(dāng)前工作目錄。
pype:顯示一個(gè)數(shù)據(jù)結(jié)構(gòu)(如一個(gè)結(jié)構(gòu)或C++類)的內(nèi)容。
quit:退出gdb。
reverse-search:在源文件中反向搜索正規(guī)表達(dá)式。
run:執(zhí)行該程序。
search:在源文件中搜索正規(guī)表達(dá)式。
set variable:給變量賦值。
signal:將一個(gè)信號(hào)發(fā)送到正在運(yùn)行的進(jìn)程。
step:執(zhí)行下一個(gè)源程序行,必要時(shí)進(jìn)入下一個(gè)函數(shù)。
undisplay display:命令的反命令,不要顯示表達(dá)式。
until:結(jié)束當(dāng)前循環(huán)。
up:上移棧幀,使另一函數(shù)成為當(dāng)前函數(shù)。
watch:在程序中設(shè)置一個(gè)監(jiān)測(cè)點(diǎn)(即數(shù)據(jù)斷點(diǎn))。
whatis:顯示變量或函數(shù)類型。
1.8編譯程序常見的錯(cuò)誤與問題
在編寫程序時(shí),無論是邏輯上還是語法上,不可能一次做到完全正確。于是在編譯程序
時(shí),就會(huì)發(fā)生編譯錯(cuò)誤。本節(jié)將講述程序編譯時(shí)常見的錯(cuò)誤類型與處理方法。
1.8.1邏輯錯(cuò)誤與語法錯(cuò)誤
在編程時(shí),出現(xiàn)的錯(cuò)誤可能有邏輯錯(cuò)誤和語法錯(cuò)誤兩種。這兩種錯(cuò)誤的發(fā)生原因和處理
方法是不同的。本節(jié)將講述這兩種錯(cuò)誤的處理方法。
邏輯錯(cuò)誤指的是程序的設(shè)計(jì)思路發(fā)生了錯(cuò)誤。這種錯(cuò)誤在程序中是致命的,程序可能
正常編譯通過,但是結(jié)果是錯(cuò)誤的。當(dāng)程序正常運(yùn)行而結(jié)果錯(cuò)誤時(shí),一般都是編程的
思路錯(cuò)誤。這時(shí),需要重新考慮程序的運(yùn)算方法與數(shù)據(jù)處理流程是否正確。
語法錯(cuò)誤:語法錯(cuò)誤指的是程序的思路正確,但是在書寫語句時(shí),發(fā)生了語句錯(cuò)誤。
這種錯(cuò)誤一般是編程時(shí)不小心或是對(duì)語句的錯(cuò)誤理解造成的。在發(fā)生語句錯(cuò)誤時(shí),程
序一般不能正常編譯通過。這時(shí)會(huì)提示錯(cuò)誤的類型和錯(cuò)誤的位置,按照這些提示改正
程序的語法錯(cuò)誤即可完成錯(cuò)誤的修改。
1.8.2 C程序中的錯(cuò)誤與異常
C程序中的錯(cuò)誤,根據(jù)嚴(yán)重程序的不同,可以分為異常與警誤兩類。在編譯程序時(shí),這
兩種情況對(duì)編譯的影響是不同的,對(duì)錯(cuò)誤與異常的處理方式是不同的。
1.什么是異常
異常指的是代碼中輕微的錯(cuò)誤,這些錯(cuò)誤一般不會(huì)影響程序的正常運(yùn)行,但是不完全符
合編程的規(guī)范。在編譯程序時(shí),會(huì)產(chǎn)生一個(gè)“警告”,但是程序會(huì)繼續(xù)編譯。下面的程序會(huì)使
程序發(fā)生異常,在編譯時(shí)產(chǎn)生一個(gè)警告錯(cuò)誤。
在除法中,0作除數(shù)。
在開方運(yùn)算時(shí),對(duì)負(fù)數(shù)開平方。
程序的主函數(shù)沒有聲明類型。
程序的主函數(shù)沒有返回值。
程序中定義了一個(gè)變量,但是沒有使用這個(gè)變量。
變量的存儲(chǔ)發(fā)生了溢出。
2.什么是錯(cuò)誤
錯(cuò)誤指的是程序的語法出現(xiàn)問題,程序編譯不能正常完成,產(chǎn)生一個(gè)錯(cuò)誤信息。這時(shí)會(huì)
顯示錯(cuò)誤的類型與位置。根據(jù)這些信息可以對(duì)程序進(jìn)行修改。
1.8.3編譯中的警告提示
在編譯程序時(shí),如果發(fā)生了不嚴(yán)重的異常,會(huì)輸出一個(gè)錯(cuò)告錯(cuò)誤,然后完成程序的編譯。
例如下面的內(nèi)容是一個(gè)程序在編譯時(shí)產(chǎn)生的警告。
5.1.c:In function'main':
5.1.c:16:警告:‘main’的返回類型不是‘int’
5.1.c:18:警告:被零除
這些的含義如下所示。
(1)“In function'main':”表示發(fā)生的異常在main函數(shù)內(nèi)。
(2)“5.1.c:16:”表示發(fā)生異常的文件是5.1.c,位置是第16行。
(3)下面的信息是第16行的異常,表明程序的返回類型不正確。
‘main’的返回類型不是‘int’
(4)下面的警告信息表明程序的第18行有除數(shù)為0的錯(cuò)誤。
5.1.c:18:警告:被零除
1.8.4找不到包含文件的錯(cuò)誤
程序中的包含文件在系統(tǒng)或工程中一定要存在,否則程序編譯時(shí)會(huì)發(fā)生致命錯(cuò)誤。例如
下面的語句包含了一個(gè)不正確的頭文件。
#include<stdio1.h>
編譯程序時(shí),會(huì)發(fā)生錯(cuò)誤,錯(cuò)誤信息如下所示。
5.1.c:2:20:錯(cuò)誤:stdio2.h:沒有那個(gè)文件或目錄
1.8.5錯(cuò)誤地使用逗號(hào)
程序中逗號(hào)的含義是并列幾個(gè)內(nèi)容,形成某種算法或結(jié)構(gòu)。程序中如果錯(cuò)誤地使用逗號(hào),會(huì)
使程序在編譯時(shí)發(fā)生致命錯(cuò)誤。例如下面的代碼,是程序中的if語句后面有一個(gè)錯(cuò)誤的逗號(hào)。
int max(int i,int j)
{
if(i>j),
{
return(i);
}
else
{
return(j);
}
}
程序編譯時(shí)輸出的錯(cuò)誤信息如下所示。表明max函數(shù)中逗號(hào)前面的表達(dá)式有錯(cuò)誤,實(shí)際
上的錯(cuò)誤是多一個(gè)逗號(hào)。
5.1.c:In function‘max’:
5.1.c:4:錯(cuò)誤:expected expression before‘,’token
5.1.c:In function‘max’:
1.8.6括號(hào)不匹配錯(cuò)誤
程序中的引號(hào)、單引號(hào)、小括號(hào)、中括號(hào)、大括號(hào)等符號(hào)必須成對(duì)出現(xiàn)。這方面的錯(cuò)誤
會(huì)使程序發(fā)生符號(hào)不匹配的錯(cuò)誤。發(fā)生這種錯(cuò)誤后,編譯程序往往不能理解代碼的含義,也
不能準(zhǔn)確顯示錯(cuò)誤的位置,而是顯示表達(dá)式錯(cuò)誤。例如下面的代碼,在最后一行上了一個(gè)花
括號(hào)。
int max(int i,int j)
{
if(i>j)
{
return(i);
}
else
{
return(j);
}
編譯程序時(shí),會(huì)顯示下面的錯(cuò)誤信息。
5.1.c:22:錯(cuò)誤:expected declaration or statement at end of input
1.8.7小括號(hào)不匹配錯(cuò)誤
程序中的小括號(hào)一般在一行內(nèi)成對(duì)出現(xiàn)并且相匹配。小括號(hào)不匹配時(shí),程序發(fā)生致命錯(cuò)
誤。例如下面的代碼,第一行多了一個(gè)右半邊括號(hào)。
if(i>j))
{
return(i);
}
else
{
return(j);
}
編程程序時(shí),會(huì)發(fā)生下面的錯(cuò)誤。顯示括號(hào)前面有錯(cuò)誤,并且導(dǎo)致下面的else語句也有
錯(cuò)誤。
5.1.c:4:錯(cuò)誤:expected statement before‘)’token
5.1.c:8:錯(cuò)誤:expected expression before‘else’
1.8.8變量類型或結(jié)構(gòu)體聲明錯(cuò)誤
程序中的變量或結(jié)構(gòu)體的名稱必須正確,否則程序會(huì)發(fā)生未聲明的錯(cuò)誤。例如下面的代
碼,用一個(gè)不存在的類型來聲明一個(gè)變量。
ch a;
程序在運(yùn)行時(shí),會(huì)顯示出這個(gè)變量錯(cuò)誤,并且會(huì)顯示有其他的錯(cuò)誤。
5.1.c:17:錯(cuò)誤:‘ch’未聲明(在此函數(shù)內(nèi)第一次使用)
5.1.c:17:錯(cuò)誤:(即使在一個(gè)函數(shù)內(nèi)多次出現(xiàn),每個(gè)未聲明的標(biāo)識(shí)符在其
5.1.c:17:錯(cuò)誤:所在的函數(shù)內(nèi)只報(bào)告一次。)
5.1.c:17:錯(cuò)誤:expected‘;’before‘a(chǎn)’
1.8.9使用不存在的函數(shù)的錯(cuò)誤
如果程序引用了一個(gè)不存在的函數(shù),會(huì)使用程序發(fā)生嚴(yán)重的錯(cuò)誤。例如下面的代碼,引
用了一個(gè)不存在的函數(shù)add。
k=add(i,j);
程序顯示的錯(cuò)誤信息如下所示,表明在main函數(shù)中的add函數(shù)沒有定義。
/tmp/ccYQfDJy.o:In function`main':
5.1.c:(.text+0x61):undefined reference to`add'
collect2:ld返回1
5.8.10大小寫錯(cuò)誤
C程序?qū)Υa的大小寫是敏感的,不同的大小寫代表不同的內(nèi)容。例如下面的代碼,將
小寫的“int”錯(cuò)誤的寫成了“Int”。
Int t;
程序顯示的錯(cuò)誤信息如下所示,表明“Int”類型不存在或未聲明。發(fā)生這個(gè)錯(cuò)誤時(shí),會(huì)
輸出多行錯(cuò)誤提示。
5.1.c:16:錯(cuò)誤:‘Int’未聲明(在此函數(shù)內(nèi)第一次使用)
5.1.c:16:錯(cuò)誤:(即使在一個(gè)函數(shù)內(nèi)多次出現(xiàn),每個(gè)未聲明的標(biāo)識(shí)符在其
5.1.c:16:錯(cuò)誤:所在的函數(shù)內(nèi)只報(bào)告一次。)
5.1.c:16:錯(cuò)誤:expected‘;’before‘t’
1.8.11數(shù)據(jù)類型的錯(cuò)誤
程序中的某些運(yùn)算,必須針對(duì)相應(yīng)的數(shù)據(jù)類型,否則這個(gè)運(yùn)算會(huì)發(fā)生數(shù)據(jù)類型錯(cuò)誤。例
如下面的代碼,錯(cuò)誤地將兩個(gè)整型數(shù)進(jìn)行求余運(yùn)算。
float a,b;
a=a%b;
程序編譯時(shí),輸出下面的錯(cuò)誤,表明“%”運(yùn)算符的操作數(shù)無效。
5.1.c:19:錯(cuò)誤:雙目運(yùn)算符%操作數(shù)無效
1.8.12賦值類型錯(cuò)誤
任何一個(gè)變量,在賦值時(shí)必須使用相同的數(shù)據(jù)類型。例如下面的代碼,錯(cuò)誤地將一個(gè)字
符串賦值給一個(gè)字符。
char c;
c="a";
程序編譯時(shí)的結(jié)果如下所示,表明賦值時(shí)數(shù)據(jù)類型錯(cuò)誤。
5.1.c:19:警告:賦值時(shí)將指針賦給整數(shù),未作類型轉(zhuǎn)換
1.8.13循環(huán)或判斷語句中多加分號(hào)
分號(hào)在程序中的作用是表示一個(gè)語句結(jié)束。在程序的語句中用一個(gè)單獨(dú)的分號(hào)表示一個(gè)
空語句。但是在循環(huán)或判斷結(jié)構(gòu)的后面,一個(gè)分號(hào)會(huì)導(dǎo)致程序的邏輯發(fā)生錯(cuò)誤。關(guān)于這些結(jié)
構(gòu)的使用方法,后面的章節(jié)將會(huì)詳細(xì)講到。下面的程序,在for語句的后面,錯(cuò)誤的添加了一
個(gè)分號(hào),導(dǎo)致程序不能正常地進(jìn)行循環(huán)。
#include<stdio.h>
main()
{
int sum,j;
sum=0;
for(j=0;j<11;j++);
{
sum=sum+j;
}
printf(“%d”,sum);
}
這個(gè)程序的本意是要求出10以內(nèi)的整數(shù)和。但是在for語句的后面,錯(cuò)誤地使用了一個(gè)
分號(hào)。這時(shí),程序不能正確地進(jìn)行循環(huán),而是把分號(hào)作為一個(gè)語句進(jìn)行循環(huán),所以程序輸出
的結(jié)果為“11”。
1.9小結(jié)
程序的編譯和調(diào)試是編程的一個(gè)重要環(huán)節(jié)。本章講解了Linux系統(tǒng)中C編程的編譯器gcc
和編譯器gdb的使用。使用gcc時(shí),需要對(duì)編譯進(jìn)行各種設(shè)置,需要理解gcc各項(xiàng)參數(shù)的作
用。gdb的學(xué)習(xí)重點(diǎn)是gdb單步運(yùn)行程序的理解,通過程序的單步運(yùn)行發(fā)現(xiàn)程序中的問題。
總結(jié)
以上是生活随笔為你收集整理的GCC的编译和调试--入门介绍的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 程序员第一定律:关于技能与收入
- 下一篇: Aspose.Cells使用总结大全