UNIX下C程序的编译与调试.docx
- 文档编号:4978490
- 上传时间:2022-12-12
- 格式:DOCX
- 页数:17
- 大小:28.92KB
UNIX下C程序的编译与调试.docx
《UNIX下C程序的编译与调试.docx》由会员分享,可在线阅读,更多相关《UNIX下C程序的编译与调试.docx(17页珍藏版)》请在冰豆网上搜索。
UNIX下C程序的编译与调试
三、UNIX下C程序的编译与调试
3.1CC编译器
我们用cc来编译生成目标文件,或者生成可执行文件。
其实cc可以生成包括.o目标文件、.i预处理文件等在内的中间过程文件。
中间过程文件可以由后缀指出其类型:
.c:
C源文件。
默认处理过程是预处理、编译、汇编。
.C:
C++源文件。
默认处理过程是预处理、编译、汇编。
.cc:
C++源文件。
默认处理过程是预处理、编译、汇编。
.cxx:
C++源文件。
默认处理过程是预处理、编译、汇编。
.m:
Objective-C源文件。
默认处理过程是预处理、编译、汇编。
.i:
经过预处理的C文件。
默认处理过程是编译、汇编。
.ii:
经过预处理的C++文件。
默认处理过程是编译、汇编。
.s:
汇编器的源文件。
默认处理过程是汇编。
.S:
汇编器的源文件。
默认处理过程是预处理、汇编。
.h:
预处理器需要的头文件。
一般不出现在命令行上。
其他后缀的文件将送给连接器(linker)。
一般包括:
.o:
目标文件。
.a:
归档文件(库文件)。
链接过程是上面处理过程的最后一步,除非指定了-c、-S或-E任选项。
一般cc的命令格式如下:
cc[option|filename]
每个任选项由“-”开头,每个任选项分开使用。
例如:
任选项-dr与任选项-d-r完全不同。
下面看一些常用的任选项:
-c只编译不连接,生成.o文件。
编译器对每个源文件输出与该文件相符的目标文件。
常常用于编译不包含主程序的子程序文件。
-ooutputfile指定输出文件的名字outputfile。
不管指定名字的文件是一个可执行
文件、目标文件、汇编文件还是预处理文件。
缺省的输出可执行文件是a.out。
如果使用了-c参数,则缺省的输出文件为将源程序文件名的后缀改为.o。
-g在编译时产生额外的符号表(调试信息),同时将选项-lg传给连接程序(ld)以
使它连接g库(/usr/lib/libg.a),从而使程序可用dbx调试。
-Ipathname在搜索include文件的目录列表中增加新的目录。
除非源程序中指定
了绝对路径,否则cc在编译时将先后在源程序所在的路径、由-I指定的路径和缺省路径/usr/include中寻找源程序中#include的文件。
-llibrary连接指定的函数库。
比如用-lm连接数学库。
被连接的库将是库函数路
径中的lib**.so或lib**.a,其中**为-l后的字符串。
-Ldirectory在缺省的库函数路径(usr/lib)外增加另外的库函数寻找路径。
-temp=directory指定编译连接时采用目录directory作为存放临时文件的路径。
如不指定,则临时路径为/tmp,当/tmp较小或已经较满时可能无法编译较大的程序,此时需指定temp路径。
-w在编译时不列出警告信息。
-W显示额外的警告消息。
-O优化执行代码,有的编译器还可以通过指定-O1、-O2、-O3等来指定优化的
程度。
-S在编译步骤后停止,不进行汇编。
输出的是一个可由汇编器(assembler)汇
编的文件。
一般情况生成的可汇编文件名由源文件(文件名后缀为“.c”、“I”等)的更改为.s为后缀的文件。
-E在预处理后停止,不进行编译。
输出文件是预处理过的代码,并送往标准输
出。
-v在标准错误输出上显示编译器执行各步骤的命令。
也显示版本信息。
-I-在-I-任选项之前由-I指定的任何目录只用来搜索#include“头文件名”,而
不用来搜索#include<头文件名>。
命令行中-I-后的-I任选项指出的目录可以用来搜索所有#include包含的头文件。
-Dmacro定义宏macro,宏macro的值为字符串“1”。
-Dmacro=defn定义宏macro,其值为defn。
所有命令行上的-D任选项在-U任选项
之前处理。
-Umacro取消宏macro的定义。
该任选项在-D任选项之后处理,但在-include任
选项和-imarcos任选项之前处理。
-static当系统支持共享库链接时,才支持该任选项。
该任选项阻止链接器链接
共享库。
-shared产生一个可共享的目标,其他目标文件链接该目标形成可执行文件。
举例:
1、如果所有程序都在一个文件myprog.c中,那么只需用
ccmyprog.c
即可完成编译连接的全过程,所生成的执行文件是a.out。
2、如果源程序由两个文件mymain.c和mysubs.c组成,程序中使用了系统的数学库,我们希望生成的执行文件叫myprog,则编译连接的命令为:
ccmymain.cmysubs.c–lm–omyprog
3.2dbx调试器
Unix系统下有若干调试工具软件,比较常用的有dbx。
目前还常见的是GNU的gdb和X下图形界面的调试工具ddd。
dbx是一种源程序级的调试工具。
虽然dbx不象在IDE(集成开发环境)中那样使用热键,但对于IDE中各种功能在dbx中都可以找到等同的命令。
要使用dbx调试程序,首先在编译生成执行文件时要加上调试信息,即使用-g任选项。
否则dbx调试时,没有符号表,将只能看到汇编命令。
例如:
cc-gexample.cprogmain.c-omyprog
这样在dbx中就可以按源代码跟踪。
注意:
dbx不仅可以用于调试C程序,对其他语言的程序也可以用dbx调试,而且可以对汇编程序跟踪。
3.2.1运行dbx
dbx命令的格式为:
dbx[options][prognamecorefile]]
例如:
%dbxa.out
%dbxmyprogcore
指定core文件,可以查看程序是在执行什么语句时coredump的。
dbx启动后,如果没有指定core文件,dbx会在当前目录中查看是否有core文件;如果没有就不使用core文件。
如果在命令中没有指定progname或corefile,可以在启动后,用givenfile和corefile命令设定。
启动dbx时,dbx会在用户目录下查找.dbxinit配置脚本;如果没有找到,则使用系统缺省的设置。
在.dbxinit文件中,包含的是一些dbx命令,dbx启动时,首先执行这些命令。
例如编辑.dbxinit文件为:
set$editor=vi
set$lines=20
aliasnnext
除了使用.dbxinit来设置dbx外,可以用环境变量DBXINIT来设置选项。
dbx会将DBXINIT插入到命令行选项之前。
下面看看部分dbx的命令行任选项:
-Idir指定在dir目录搜索源程序文件。
-cfile指定file为启动时执行的命令文件,代替使用.dbxinit文件
-pPID#调试指定进程号为PID的进程。
-pname调试指定名字为name的进程(由ps可以看到)。
3.2.2dbx命令
进入dbx后就可以交互的使用dbx命令了。
下面介绍一些常用的dbx命令:
givenfile[filename]当该命令没有参数时,显示当前调试的程序文件名,当给出filename参数时,读入新的filename程序的符号表,并等待调试。
corefile[filename]当该命令没有参数时,显示当前调试的core文件名,否则用新的filename代替原来的core文件。
usedir指定dir目录为唯一的源程序所在路径。
dirdir在源程序所在路径中新增一个dir路径
filesourceprog.c指定当前调试程序的源程序文件为sourceprog.c
list[n1[:
n2]]列出源代码的若干行。
如果不指定参数,则列出当前行以下若干行。
仅指定n1时,列出从行号为n1后的若干行。
指定参数n1:
n2时,列出行号n1到行号n2之间的程序源代码。
whatissymbol用来检查程序中的变量和子程序的类型定义。
/string从当前行开始在源代码中向下查找string字符串,就象vi中的一样。
查找到后,改变当前行为查找到的行。
?
string从当前行开始在源代码中向上查找string字符串,就象vi中的一样。
查找到后,改变当前行为查找到的行。
editsource.c使用指定的编辑器编辑source.c文件。
run[args]执行被调试的程序(在遇到断点时停止)。
参数args将传递给执行程序,相当于执行程序的命令行参数。
rerun按上次的执行参数,再次执行被调试的程序。
stopat[filename:
]n1[ifexp]在行号n1所在语句处设置断点,如果指定filename参数则表示哪个源文件的n1行。
例如:
stopat“source.c”:
55。
参数ifexp是条件控制,即当exp为真时,才设置断点。
例如:
stopat55ifi==22,即当程序中变量i等于22时,在55行设置断点。
stopinprocname[ifexp]在进入子程序procname()后设置断点。
条件控制的使用与stopat相同。
when[atn1]|[inprocname][ifexp]{dbx命令}当运行到n1行或procname()子程序处或ifexp条件满足时,执行dbx命令。
assignvar=value给程序中的变量赋值。
printexp显示变量或表达式的值。
alias[str1][str2]定义别名。
如果参数为空,则列出已有别名定义。
例如:
aliasrrun执行后,可以用r代替run命令。
unaliasaliasname取消别名aliasname的设置。
history类似于CShell中的history命令。
cont[toprocname|n1]从当前的停止处继续执行程序。
当使用参数时,表示继续执行到指定的子程序或者行号处。
dump[proc]不带参数时,列出所有进程的全局变量信息。
指定子程序时,列出proc中的变量信息。
down[n]程序执行的堆栈移到下一层(或n层)。
funcproc移到指定的子程序中。
goton1转移到指定的行。
next[n]向下执行一条(或n条)语句。
注意next不进入子程序中,把子程序当成一条语句。
step[n]向下执行一条(或n条)语句。
注意step跟踪进入子程序中。
trace{[var]atn1[ifexp]|[var]inproc[ifexp]}在指定行n1或指定子程序proc中跟踪或条件exp为真跟踪程序的运行(或程序运行时的变量变化)。
printregs显示当前寄存器的值。
help[command]不带参数时,显示dbx所有的命令。
指定参数时,显示指定命令的帮助信息。
quit退出dbx。
3.3make
设计make程序起初是为了避免不必要的重复编译。
使用make不仅可以在一个工程的开发阶段避免对同样命令的多次键入,而且对维护程序文件也十分有用,尤其对于依赖关系,make可以非常有效地维护任何有相互依赖性的多个文件集合。
它可以用来维护C、C++或HTML源代码,对于有依赖关系的文件集都可以起到一定的维护能力。
它提供了编写文件依赖性的一个规则,和对已经改变了的文件执行什么命令来维护或编译。
它提供了维护文件的一个强大的、非过程的和基于模板的方法,用户只需告诉make要做什么并提供一些规则,然后由make来做剩下的工作。
这里介绍make的常规用法和目的,即使用make和makefile来维护我们开发过程中的文件集合。
使用make来维护.o文件和可执行文件,有利于一致地编译程序。
它根据程序中模块的修改情况,自动判断应对哪些模块重新编译,保证软件由最新的模块构成。
3.3.1makefile规则
make命令执行的操作依赖于当前目录中的名为makefile或Makefile的文件(缺省的文件名,用户也可以指定别的文件名),这一文件描述系统中各模块之间的相互依赖关系,以及要产生程序的目标文件所要执行的命令。
make的基本思想是为每一模块都设置一个时间标记,根据时间标记和依赖关系来决定哪些文件需要更新。
makefile是一个make的规则描述脚本。
它描述能生成什么文件集并怎样生成,它允许有四种类型行:
目标行、命令行、宏定义行和make伪指令行(如“incldude”)。
makefile文件中注释以“#”开头。
当一行写不下时,可以用“\”字符转入下一行。
目标行
目标行告诉make建立什么。
它由一个目标名表后面跟冒号“:
”,再跟一个依赖性表组成。
目标名表可以包含多个目标的名字,一般情况下只列出一个目标名。
目标名表不能为空。
依赖性表,指出目标和哪些其他文件或目标具有依赖关系。
依赖性表可以为空。
下面是目标行的一个例子:
example:
depfiledeptarget
该目标行指出目标example与depfile和deptarget有依赖关系,如果depfile和deptarget之一修改,则应该重新生成目标。
examp1examp2examp3:
deptarget1deptarget2depfile
该目标行指出目标名表中的examp1、examp2、examp3这三个各自独立的目标是用相同的依赖列表和规则生成的。
clean:
空的依赖列表说明目标clean没有其他依赖关系。
make执行时,按依赖深度优先顺序扫描处理遇到的目标。
即当前目标的依赖表中有其他目标名,则立刻去扫描该目标,直到当前目标依赖表中的目标都扫描过后,才确定当前目标是否需要重新生成。
如果有比当前目标更新的依赖目标,或依赖的目标不存在,则必须生成相关目标。
注意依赖表为空,则总是生成目标。
如果目标依赖的文件或目标名比当前目标旧,则不生成目标。
目标行后续的以Tab开始的行是指出目标生成规则。
该Tab字符不能以空格代替。
例如:
example.o:
example.cexample.h
cc-cexample.c
该例子指出目标example.o依赖于example.c和example.h。
如果example.c或example.h其中之一改变了,就需要重新生成目标example.o,如果要重新生成目标,则要执行命令cc-cexample.c如果example.c和example.h都没有改动,则不生成目标。
可以用文件名模式匹配来自动地为目标生成依赖表,可以使用shell通配符“*”、问号“?
”和中括号“[]”。
例如makefile所在的目录下,所有以.c结尾的文件:
main.ccheck.ctreatment.c
则make下面二个目标的效果是等同的:
prog:
main.ccheck.ctreatment.c
prog:
*.c
一般地,包含依赖元素的列表用于构造一个目标。
如依赖性表可用来组成一个目标文件。
目标文件能构成可执行文件:
Program:
Part.ocomponent.omodule.o
或目标依赖的头文件和源文件:
module.o:
module.cmodule.hpart.h
下面看一个简单的makefile的例子:
all:
hellohello1
hello:
hello.o
cc-ohello-L/usr/X11R6/lib-L/usr/lib-lXm-lXt-lX11hello.o
hello1:
hello1.o
cc-ohello1-L/usr/X11R6/lib-L/usr/lib-lXm-lXt-lX11hello1.o
hello.o:
hello.c
cc-chello.c
hello1.o:
hello1.c
cc-chello1.c
clean:
rm*.ohellohello1
make使用makefile文件时,从第一个目标开始扫描,从这个例子中我们看到,make程序将先看目标all的依赖关系表,查看依赖表中的依赖目标是否比目标新,如果目标存在并且不是旧的,则不生成新的目标。
如果目标不存在则生成新的目标。
这里因为没有all文件,所以查看依赖表。
首先扫描hello,如果目标hello也不存在,则查看hello的依赖表。
hello依赖hello.o,这里假设hello.o也不存在或比目标新,那么再扫描hello.o目标,而hello.o目标依赖hello.c文件,如果hello.c文件自上次make以来没有改动则hello.o不需要改动,这时,返回去,由cc-ohello-L/usr/X11R6/lib-L/usr/lib-lXm-lXt-lX11hello.o生成目标hello,然后返回all目标再看依赖目标hello1。
如果hello.c修改过则由命令cc-chello.c来生成目标hello.o。
这里的例子,没有使用隐含规则。
在执行make时,make总是从第一个目标开始。
所以目标clean不会自动被执行。
可以用命令行makeclean来生成目标,因为目标的依赖表为空,所以makeclean,总是要求生成目标。
而生成目标的命令是rm*.ohellohello1所以生成目标就是删除了hello.o、hello1.o、hello、hello1四个文件。
使用后缀规则的目标
make有自己规定的通用目标,也就是后缀规则。
后缀规则是一次性告诉make如何建立某些类型目标。
前例中有许多重复内容。
例如,生成hello和hello1的命令类似,生成hello.o和hello1.o的命令类似。
除了编译或链接的文件不一样,其他都一样。
这时,我们就可以使用后缀规则。
下面的.c.o规则告诉make怎样把以.c结尾的文件转换为以.o结尾的文件:
.c.o:
cc-c$<
在这个.c.o规则中用到了宏“$<”,它以当前的依赖文件名代入规则中。
有关这些特殊的宏将在后面介绍。
把.c.o规则用到前面的例子中可以简化为:
LDFLAGS=-L/usr/X11R6/lib-L/usr/lib-lXm-lXt-lX11
.SUFFIXES:
.SUFFIXES:
.c.o
.c.o:
$(CC)-c$<
all:
hellohello1
hellohello1:
hello.ohello1.o
$(CC)$(LDFLAGS)$@.o-o$@
这里也用到了宏定义,这些都将在后面介绍。
现在我们看看用make时,屏幕的输出:
%make
cc-chello.c
cc-chello1.c
cc-L/usr/X11R6/lib-L/usr/lib-lXm-lXt-lX11hello.o-ohello
cc-L/usr/X11R6/lib-L/usr/lib-lXm-lXt-lX11hello1.o-ohello1
这时,已经编译好了hello和hello1两个执行文件。
如果这时再执行make将得到:
Nothingtobedonefor`all'.
这是因为目标all依赖的两个目标hello和hello1,因为目标all不存在,所以要检查它的依赖表,而它依赖的hello和hello1两个目标已经是最新的,它们依赖的hello.o和hello1.o都不比hello和hello1新,所以目标hello和hello1不需要再次生成,而all目标没有指定如何生成all(实际上我们也不需要生成目标all文件)。
make提供了许多默认缺省的后缀规则,那么make是如何使用后缀规则的呢?
用下述方法能决定用哪个后缀规则:
首先,make中文件类型由它的后缀(扩展名)定义,例如C语言源文件就用.c作为后缀,目标文件(未链接的文件)以.o为后缀。
make搜索默认缺省的后缀规则表和用户指定的后缀规则表,如果找到哪个可以建立所请求文件类型的规则,而且规定类型的文件存在,则应用规则。
例如,如果一个makefile仅规定了两个规则,顺序为“.c.o”和“.cc.o”两个规则,则make生成目标foo.o时,使用后缀规则的步骤为:
make首先试图用.c.o规则,因为目标foo.o的基名是foo,根据.c.o规则查找默认的目录下是否有foo.c,如果有则编译。
如果foo.c不存在,则不能应用.c.o规则。
这时试图用.cc.o规则,根据.cc.o规则查找默认的目录下是否有foo.cc,如果有则编译。
如果foo.cc不存在,则报不能生成目标错误。
有的make不支持后缀规则的传递闭包,也就是说如果存在后缀规则从.x文件生成.y文件,还存在另外一个后缀规则从.y文件生成.z文件,make不会把这两个规则组合起来形成传递,也就是说make不会从.x文件生成.z文件。
这时要指定作为目标的中间步骤。
但有的版本的make支持后缀规则的传递,他们将它称为规则链接。
双后缀规则与单后缀规则
前面介绍的规则都是双后缀规则,即它们包括两个后缀,如.c.o,用来把一个C源文件编译为目标文件。
双后缀规则描述的是如何由第一个后缀类型的文件生成第二个后缀类型的文件。
例如:
.c.o规则描述如何由.c文件生成.o文件。
单后缀规则描述了怎样由指定后缀的文件生成由它基名为名字的文件。
例如使用单后缀规则.c,可以由example.c生成example文件。
在单后缀规则中,事实上第二个后缀是空。
make有关C的标准后缀规则:
.c
$(LINK.c)-o$@$<$(LDLIBS)
.c.ln
$(LINK.c)$(POUTPUTOPTPUTOPTION)-i$<
.c.o
$(COMPILE.c)$(OUTPUTOPTION)$<
.c.a
$(COMPILE.c)-o$%$<$(AR)$(ARFLAGS)$@$%$(RM)$%
特殊目标
make提供了几个特殊目标来设置make的行为,它们中的一些可以为它指定依赖表,但实际上指定的依赖表是特殊目标的参数,例如:
.SUFFIXES的依赖表指出的是新增的后缀规则。
下面列出了一些特殊目标:
.IGNORE:
增加该目标后,make将忽略建立目标时命令行返回非零的错误码。
当命令行返回非零值时,make缺省动作是停止并退出。
.SILENT:
增加该目标后,make执行的命令行不再显示。
缺省时,make显示命令行后执行命令行,但是对前面有“@”号的命令行不显示。
.DEFAULT:
如果没有应用别的规则,make就执行.DEFAULT的命令行。
.PRECIOUS:
当收到一个信号或从shell命令返回的非零代码时make删除它所有已建立的文件。
但有些文件即使出了错误,用户也不想让make删除,这些文件可以作为.PRECIOUS目标的参数。
它可以在一个makefile中出现多次,每一次都累积文件列表。
.SUFFIXES:
它为make指定新的后缀规则,新的后缀规则是作为.SUFFIXES的依赖表来给出的。
.SUFFIXES可以在一个makefile中多次使用,每一次都将新的后缀规则加入以前的后缀规则中。
例如:
.SUFFIXES:
.ms.tr。
但.SUFFIXES的依赖表为空时,将重设置后缀规则表为空。
后缀的顺序很重要。
因为它是尝试规则的顺序。
为了重新排列顺序,使用空依赖表的.SUFFIXES目标来清空当前后缀表,然
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- UNIX 程序 编译 调试
![提示](https://static.bdocx.com/images/bang_tan.gif)