【文档说明】第四讲LINUX系统的C编程课件.ppt,共(70)页,226.399 KB,由小橙橙上传
转载请保留链接:https://www.ichengzhen.cn/view-45504.html
以下为本文档部分文字说明:
第四讲LINUX系统的C编程主要内容编译器及工作过程链接器与库文件静态库的构造及使用共享库的构造及使用make命令与Makefile调试器与程序调试其它编程工具简介第四讲Linux系统的C编程4.1编译器4.2头文件4.3链接器与库文件4.4静
态库4.5共享库4.6make与Makefile4.7调试器gdb4.8UNIX/Linux其它编程工具简介4.1编译器4.1.1功能及用法4.1.2参数及说明4.1.3示例4.1.4gcc的工作过程4.1.1功能及用法1.为什么要使用编译
器?C语言源程序需要经过编译和链接这两个过程才能转换成二进制可执行程序。一般在Unix系统中使用的C编译器是cc(CCompiler的缩写)。在各个Linux发行版本中广泛使用的C编译器名为gcc(GNUcc)。2.功能gcc能将C/C++
源程序和目标程序编译并调用链接程序ld生成可执行文件,如果用户没有给出可执行文件的名字,gcc将默认生成一个名为a.out的可执行文件。4.1.1功能及用法3.用法gcc的一般用法为:gcc[options]<filenames>其常用格式为:gcc[-c][-S]
[-E][-s][-g][-static][-shared][-rdynamic][-Idir…][-Ldir…][-lmylib][-xLanguage][-Olevel][-Dmacro[=defn]…][-Umacro][-m
machine-option…][-oout_file]infile…几点说明gcc根据源程序的后缀名来决定使用哪一种语言的编译器进行编译工作。后缀名为“.c”(小写)的文件被gcc认为是C语言的源程序文件。例如:gcchello.cgcc编译出来的可执行程序默认是a.ou
t。几点说明(续)g++是一个C++版本的gcc编译器。g++要求C++语言源程序文件带有后缀名“.cc”。例如:g++hello.cc4.1.2参数及说明参数意义-c编译后仅输出*.o型的目标文件,而不链接生成可执行程序-S编译后仅生成汇编语言文件*.s,但不生成目标文
件和可执行代码-s生成可执行文件时,删除符号表和重定位信息。生成成品软件时使用-E在预处理过程后结束,不进行编译和链接,也不生成可执行代码-g在可执行文件中加入调试信息,便于程序的调试-Idir将目录dir添加到头文件搜索范围-lmylib链接时搜索库libmylib.
a-Ldir将目录dir添加到库文件搜索范围-ooutfile指定输出文件名。若不指定则采用默认方式4.1.2参数及说明(续)参数意义-mcpu=cpu-type生成与机器相关的汇编代码-O[L]编译时进行优化。L为优化级别,分别0~3和s。生成最终产品时使用-sta
tic禁止使用共享库(动态链接库)-shared生成共享库-rdynamic链接时使用共享库-Dname[=val]宏定义变量name[=val]-Uname取消宏定义变量name-xlanguage指定前端语言4.1.3示例1.C程序设有一个文件名为hello
.c的程序,其内容为:#include<stdio.h>main(){printf(“HelloWorld!\n”);}执行步骤编译可按以下方法进行编译,以生成相应的结果:gcchello.c#生成可执行程序a.out或gcc–ohellohello.c#生成可执行程序he
llo运行编译生成可执行文件之后,就可以运行了,方法为:./a.out或./hello输出结果HelloWorld!2.c++程序下面是一个c++版的HelloWorld程序,其文件名为hello.C,内容为:#includ
e<iostream.h>main(void){cout<<"Hello,World!"<<endl;}编译方法使用c++或g++来编译:g++hello.C#生成可执行程序a.outc++–ohellohell.C#生成可执行程序hello
g++–s-oHellohello.C#生成删除符号表的可执行程序Hello或使用gcc并指定库文件来编译c++程序:gcc–chello.C#生成目标文件hello.ogcc–ohhello.C–lstdc++#指定标准c++库,生成可执行程序h4.1.4gcc的工作过程使用gcc/g
++由C源代码文件生成可执行文件的过程,有以下四个阶段∶预处理(也称预编译,Preprocessing)编译(Compilation)汇编(Assembly)链接(Linking)4.2头文件在标准C中有两种形式的头文件使用方式:#include<headfile.
h>#include“headfile.h”区别:#include<headfile.h>型头文件搜索范围为默认位置/usr/include,#include“headfile.h”型头文件的搜索位置为当前目录,在Linux的GNUC中,若当前目标不存在
headfile.h,则也会到默认位置去搜索。4.3链接器与库文件UNIX/Linux的链接器为ld,其功能是将目标文件或库文件链接在一起,生成可执行文件,一般在编译过程的最后执行。Linux标准库文件一般存放
在目录/lib或/usr/lib。默认情况下链接器查找C语言的标准库函数。如果使用的不是标准的库函数,必须通过-llib或-Llibdir告诉链接器ld,否则将无法找到库函数。•库文件命名必须遵守一定命名规则,库文件名字必须永远以lib开头,后紧跟库类名,
文件名的后缀为–.a:传统静态库–.so:共享库或动态链接库•例如,libc.a为标准C库,libm.a为数学运算静态库,libc.so.6和libm.so.6分别为标准C和数学运算共享库。4.4静态库静态库也叫档案(a
rchive),以.a为后缀,用于编译链接后生成静态可执行文件。用户可以使用库管理程序ar和ranlib来创建和管理自己的或已有的静态库。4.4.1引例设有C语言文件f1.c,f2.c,f3.c,它们的内容分别为://文件f1.c的内容f1(intar
g){printf(”F1:youpassed:%d\n”,arg);}//文件f2.c的内容:f2(char*arg){printf(”F2:youpassed:%s\n”,arg);}//文件f3.c的内容:#include<stdio.h>main(){fprintf(stder
r,”Begin:\n”);f1(15);f2(”HelloWorld!”);fprintf(stderr,”:End\n”);exit(0);}可以采用各模块文件分别编译然后再统一链接的办法进行编译。cc–cf1.cf2
.c//生成f1.o和f2.occ–off3.cf1.of2.o//生成fcc–ofpf3.cf1.cf2.c//生成fp4.4.2构造和管理静态库用户可以使用命令ar构造自己的静态库:cc–cf1.cf2.c//生成目标文件f1.o和f2.oarcr
vlibmyl.af1.of2.o//生成库libmyl.aranliblibmyl.a//为子函数建立索引表说明ar用于静态库文件的管理,其功能是库创建、修改和从库中取出模块等ranlib用于为刚建立的库文件建立索引表,通过索引表
可以加快库文件搜索速度。其用法为:ranlib[-vV]ar_file4.4.3使用自己的库当用户创建自己的静态库之后,就可以按照使用系统库的方法来使用它。例如:cc–ofpf3.clibmyl.a#使用库libmyl.a和f3.c生成可执行程序fpcc–ofpf3.olibmyl.a#
使用库libmyl.a和f3.o生成可执行程序fpcc–ofpf3.c-L.–lmyl#-L指定当前目录,-lmyl指定静态库文件libmyl.a4.5共享库Linux系统的另一种库文件为共享库,用
于生成动态链接的可执行程序。共享库文件名的格式为:libNAME.so.NNAME为库名,N为版本号。可用命令ldd和ldconfig命令管理共享库。4.5.1构造共享库Linux的共享库用于生成动态链接的可执行程序。共享库
文件名的格式为:libNAME.so.NNAME为库名,N为版本号。常用命令ldd和ldconfig命令管理共享库。4.5.1构造共享库共享库构造非常简单,只需要在构造库的时候使用-shared参数就可以了。例如:用4.4.1引例中的f1.c
和f2.c构造共享库,方法是:cc-cf1.cf2.c//生成目标文件cc-shared-olibmy.sof1.of2.o//由目标文件生成共享库或cc-shared–olibmy.so–cf1.cf2.c//由源文件生成共享库4.5.2共享库的使用共享库要使用头文件dlfc
n.h和几个相关的函数:dlerror,dlopen,dlsym和dlclose。1.dlopendlopen用于打开指定共享库,并返回文件描述符。其原型为:void*dlopen(constchar*filename,intflag);dlopen调用失败时返回NULL值,否则返
回文件描述符。Dlopen函数的相关说明变量filename为共享库名。若文件名不以/开头,则为非绝对路径名,将按以下顺序搜索库文件:(1)环境变量中的LD_LIBRARY_PATH值指定的路径;(2)动态链接缓冲文件/etc/ld.so.cache;(3)库文
件默认目录/lib,/usr/lib。Dlopen函数的相关说明变量flag用来表示在什么时候解决未定义的符号,其取值范围与意义如下(1)RTLD_LAZY:指定在动态链接库的函数执行时解决;(2)RTLD
_NOW:指定在dlopen返回前就解决所有未定义的符号问题。一旦有未解决好未定义的符号,dlopen将返回NULL表示错误。注意:RTLD_LAZY和RTLD_NOW可以与RTLD_GLOBAL配合使用,使得那些在以后才加载的库可以获得其中的符号。2.dlsymdlsym用于返回共享中指
定函数的入口地址其原型为:void*dlsym(void*handle,char*symbol);dlsym根据共享库文件描述符(handle)与符号(symbol),返回symbol对应的(函数)
入口地址,相当于返回一个(函数)指针。3.dlclosedlclose用于关闭已经打开的指定共享库文件,此操作应在共享库相关操作完成之后进行。其原型为:intdlclose(void*handle);4.
dlerror函数dlerror()用于返回动态共享库操作状态信息。当共享库操作函数执行失败时,dlerror可以返回出错信息,否则返回值为NULL表示成功。其原型为:constchar*dlerror(void);5.共享库使用示
例为了使用刚创建的共享库,需要对4.4.1引例中的模块文件f3.c进行修改。假定修改后的文件被命名为f3n.c,其代码如下:#include<stdio.h>#include<dlfcn.h>#defineSO_
FILE"./libmy.so"main(){void*sfp;char*err;inttmpi=16;int(*f1)(int),(*f2)(char*);//定义函数指针sfp=dlopen(SO
_FILE,RTLD_LAZY);//打开共享库if(sfp==NULL){fprintf(stderr,dlerror());exit(1);}f1=dlsym(sfp,"f1");//获取函数f1入口地址(指针)err=dlerror();//检查是否成功if(err)
{fprintf(stderr,err);exit(2);}f2=dlsym(sfp,"f2");//获取函数f2入口地址(指针)err=dlerror();//检查是否成功if(err){fprint
f(stderr,err);exit(3);}fprintf(stderr,"-----------begine-------------\n");f2("TestString");//调用函数f2f1(tm
pi);//调用函数f1fprintf(stderr,"++++++++++++end+++++++++++++++\n");dlclose(sfp);//关闭共享库exit(0);}•编译方法为:cc–omypf3n.c–ldl
由共享库libmy.so生成可执行程序myp,-ldl则指示链接程序ld使用dl函数库。•在编译也可以使用-rdynamic参数,告诉链接程序在链接时所有函数均使用共享库。其方法为:cc–rdynamic–omypf3n.c–ldl6.共享库的管理(
1)lddldd(LibraryDependencyDisplay)用来显示一个可执行程序或共享库所使用的共享库间的依赖关系。例如:#lddmyp#ldd/usr/bin/mesg(2)ldconfig•ldconfig为共享管理程
序,其功能是在默认目录(/lib和/usr/lib)或动态库配置文件/etc/ld.so.conf内所列的目录下或指定目录下搜索共享库,进而创建出动态装入程序ld.so所需的链接和缓存文件。•缓存文件默认为/etc/ld.so.cache,其中保存有已排好
序的由/etc/ld.so.conf指定的目录内动态链接库名字列表。•为了让系统或用户动态链接库为系统所共享,需要运行ldconfig来对共享库进行配置。•ldconfig通常在系统启动时运行,而当用户安装了一个新
的动态链接库时,就需要手工运行这个命令,其用法为:ldconfig[选项…]ldconfig的使用示例如下:1)显示搜索的目录和共享库,并更新缓存文件/etc/ld.so.cacheldconfig-v默认情况下,l
dconfig不输出任何东西。使用-v参数可以显示正在扫描的目录及搜索到的共享库。2)安装共享后,更新目录/lib内的符号链接ldconfig–n/lib4.6make与Makefile4.6.1mak
e的用法简介4.6.2Makefile文件4.6.3make的用法简介4.6.4Makefile示例4.6.1make的用法简介make命令会根据Makefile的内容对项目进行管理。make能自动确定哪一个模块被修改了,然
后再进行统一、无遗漏的编译。make的用法为:make[-ffilename][options][targets]•make命令的部分参数列表如下:参数意义-Cdir在读Makefile或做任何操作前切换目录,一
般用于对目录的递归搜索-d显示调试信息-ffile指定file文件作为Makefile,而非使用默认的makefile或Makefile-Idir指定Makefile搜索目录-k默认情况下make在遇到错误将终止执行,-k
可以让在出现错误时工作的尽量长一些,以便观察分析-n让make在不真正编译的情况下列出将执行的步骤4.6.2Makefile文件Makefile文件的内容是描述项目或软件中的模块之间的相互依赖关系以及目标文件、可执行程序产生时要执行的命令等。(1)Make
file文件包含的5类内容类型意义显式规则显式告诉make如何编译或构造一个目标隐式规则隐式的通过变量或规则告诉make如何编译或构造一个目标变量定义在Makefile中可以像shell编程一样定义和使用变量命令定义完成某任务所使用的命令,一般为
shell命令注释#开始的部分。意义同shell编程规则的定义规则中的项目定义必须从最左边开始,一个规则中的第二行以后的行必须以tab健开始。规则的格式如下:targets:prerequisitescommands或targets:prerequisite
s;commandscommands(2)Makefile文件中的常用符号符号意义$@目标名$*删除了后缀的目标名$%当目标是库文件时,目标内的成员名。例如目标x.a(y.o)的目标名为x.a,成员名为y.o$^由空格分隔的目标中所有成员$?目标中的变化成员$<当前目标的第一个相关成员名Ma
kefile文件中的符号“%”可使用符号“%”定义或重定义模式规则。例如:%.o:%.ccc–c$<上述语句定义了一个规则:所有目标文件*.o依赖C语言源程序*.c,且生成方法为cc-c$<。Makefile文件中
的符号“=”或“:=”符号“=”或“:=”用于修改已经定义的变量或在已定义变量的基础上定义新变量。例如已知:var1=a.cb.cc.c则var2=$(var1:.c=.o)定义var2=a.ob.oc.o而var1+=d.c重定义var1,其值为var1=a.cb.cc.cd.c注
意“:=”与“=”是有区别的。当使用“=”时,变量将做递归或扩展“:=”只作简单替换。4.6.3make的用法简介Makefile文件内容可包含多个目标,可以通过makeobj的方式指定处理的目标,若不指定则默认为第一个。为了方便的使用Ma
kefile文件对整个项目进行编译,可在Makefile文件内设一个代表整个项目的目标,一般为all。有时为了对项目进行管理还要设置有clean、install和uninstall目标:clean用于对项目环境进行
准备,清除已经生成的目标文件等以便重新编译;install用于对整个项目的成品进行安装;uninstall则是用于对安装的项目进行卸载。4.6.4Makefile示例1.多模块项目编译示例在4.4.1引例中有三个模块,它们分别是f1.c,f2.c和
f3.c,它们之间的关系如下图按照gcc的工作过程,对模块的编译和链接过程可分为:(1)编译各源文件生成目标代码:•gcc–cf1.c•gcc–cf2.c•gcc–cf3.c(2)生成可执行程序•gcc-off1.of2.of3.o上述过程按照Makefile文件的
规定可表示为:•f:f1.of2.of3.o#f依赖于目标文件f1.o,f2.o和f3.o,其生成过程为:gcc-off1.of2.of3.o•f1.o:f1.c#f1.o依赖于f1.c,生成过程为:gcc–cf1.c•f2.o:f2.
c#f2.o依赖于f2.c,生成过程为:gcc–cf2.c•f3.o:f3.c#f3.o依赖于f3.c,生成过程为:gcc–cf3.c按照Makefile的格式要求,可以构造此项目的Makefile内容为:
f:f1.of2.of3.ogcc-off1.of2.of3.of1.o:f1.cgcc–cf1.cf2.o:f2.cgcc–cf2.cf3.o:f3.cgcc–cf3.c有了Makefile文件,可以使用make命令对此项目进行编
译。编译方法为:make或makef若要编译单个项目,比方说f1.o,可以使用以下方法:makef1.o当为Makefile文件增加all、clean和install目标时,Makefile文件可以变为:all:f#makealltobuildff:f1.of2.of3.ogcc-off
1.of2.of3.of1.o:f1.cgcc–cf1.cf2.o:f2.cgcc–cf2.cf3.o:f3.cgcc–cf3.cclean:#cleanallmyobjectfilef?.orm–ff?.oinsta
ll:all#makeallandinstallfto/usr/binwithpermission755install–m755/usr/local/bin2.多模块项目共享库编译与使用示例4.5.1共享库创建和4.5.2共享库使用的Makefile文件可分别由so.mk
和so.use来实现。so.mk的内容如下:all:libmy.so#定义all目标SRC=f1.cf2.c#定义源文件TGT=$(SRC:.c=.o)#定义目标文件%.o:%.c#定义目标文件生成规则cc-
c$<#目标文件生成命令libmy.so:$(TGT)#定义共享库生成规则cc-shared-o$@$(TGT)#共享库生成命令clean:#定义清理目标rm-f$(TGT)#清理目标目标措施so.use
的内容如下:all:mydySRC=f3.cTGT=$(SRC:.c=.o)%.o:%.ccc-c$<mydy:$(TGT)cc-o$@$(TGT)-ldlclean:rm-f$(TGT)生成目标文件可使用命令makeall–f
so.mk必要时可先运行makeclean-fso.mk使用共享库编译可使用命令makeall-fso.use4.7调试器gdb4.7.1gdb功能监视或修改程序中变量的值设置断点以使程序在指
定的代码行上暂停执行单步执行或程序跟踪4.7.2gdb基本命令命令功能命令功能break在代码里设置断点run执行当前被调试的程序bt反向跟踪,显示程序堆栈quit退出gdbc继续break后的执行watch监视一个变量的值而不管它何时被改变file装入想要调试的可执行文件set设置变量的值
kill终止正在调试的程序shell在gdb内执行shell命令list列出产生执行文件的源代码的一部分print显示变量或表达式的值next执行一行源代码但不进入函数内部where显示程序当前的调用栈step执行一行源代码而且进入函数内部4.7.3程序调试方法下面以4.
4.1引例为例,介绍Linux系统内程序调试的基本方法。1.编译时使用调试参数-gcc–g–omypf1.cf2.cf3.c2.启动gdbgdbmyp3.查看源文件(gdb)list4.设置断点(gdb)break6Breakpoint1**
***filef3.c,line65.开始执行(gdb)runBreakpoint1,main()atf3.c:66f2("TestString");6.打印变量的值(gdb)printtmpi$1=167.单步执行,
跟踪进入函数f1(也使用next命令执行f1,而不进入函数f1)(gdb)stepf1(arg=16)atf1.c:33printf(”F1:youpassed%d\n”,arg);8.显示f1()变量arg的值(gdb)p
rintarg$2arg=169.可在任何位置使用where命令,显示程序的调用栈,而得到自己位置。方法是:(gdb)where10.列当前文件的内容(gdb)list11.程序继续执行直到程序结束(gdb)continu
e