收藏 分享(赏)

gnu-gcc-开发教程.ppt

上传人:cjc2202537 文档编号:983350 上传时间:2018-05-13 格式:PPT 页数:76 大小:749KB
下载 相关 举报
gnu-gcc-开发教程.ppt_第1页
第1页 / 共76页
gnu-gcc-开发教程.ppt_第2页
第2页 / 共76页
gnu-gcc-开发教程.ppt_第3页
第3页 / 共76页
gnu-gcc-开发教程.ppt_第4页
第4页 / 共76页
gnu-gcc-开发教程.ppt_第5页
第5页 / 共76页
点击查看更多>>
资源描述

1、GNU GCC 开发教程,gnu gcc turiol,gnu gcc 简介,目前Linux下最常用的C语言编译器是GCC(GNU Compiler Collection),它是GNU项目中符合ANSI C标准的编译系统 .是Linux 的基石,操作系统内核和大部分程序都是gcc 编译的,是Linux下最重要开发工具之一gcc 早期是c的编译器, 后来发展能支持c,c+和object C,它可以通过不同的前端模块来支持各种语言,如Java、Fortran、Pascal、Modula-3和Ada等。 gcc 是一个交叉平台的编译器,目前支持几乎所有主流CPU的处理器平台.,gcc 支持的文件格式

2、,gcc 支持源码格式.c C源程序; .C,.cc,.cxx,.cpp C+源程序; .m Objective-C源程序; .i 预处理后的C文件; .ii 预处理后的C+文件; .s 汇编语言源程序; .S 汇编语言源程序;.h 预处理器文件;其它格式.o 目标文件(Object file) .a 归档库文件(Archive file),GCC 组成,gcc 一般安装在 /usr/bingcc 是一组编译工具的总称,包含如下工作C 编译器 cc,cc1,cc1plus,gccC+编译器 c+,cc1plus,g+源码预处理程序 cpp,cpp0库文件 libgcc.a,libgcc_eh.

3、a,libgcc_s.so,ibiberty.a,libstdc+,libsupc+.a,gcc的起步,生成一个hello,world程序gcc hello.c -o hello #把hello.c编译成一个可执行程序 hellogcc hello.c #不指定输出名,生成一个a.out,Hello程序,#include #define MY_NUMBER 5int main(void) printf(Hello %d,the World!n,MY_NUMBER); return 0;,编译hello,用GCC编译程序,可执行程序的构造,任何一个可执行程序从源代码到可执行的二进制程序之中都要经

4、过固定的几步预编译 (Pre-Processing) 这一步完成对预编译代码的处理编译(Compiling) 将源代码编译成汇编代码汇编(Assembling) 将汇编代码汇编成目标文件链接(Linking)将目标代码和所需要库的链成一个完整的应用程序集成开发环境(IDE)自动协助开发完成这几步,如VC+在Linux 下,如果使用命令行开发工具(gcc,ld,ar)等,需要用户手工调用这一些命令来完成这几步骤.,gcc在构建程序的作用,gcc在构建应用程序里,会调用不同的应用程序完成每一步.因此在开发中,gcc处于一个核心地位.大部分开发只需要调用gcc即可gcc所做操作Gcc 调用cpp进行

5、预处理Gcc 调用cc1进行编译,会生成汇编代码Gcc 调用as 对汇编代码,生成扩展名为.o的目标文件Gcc 调用ld 来完成对所有目标文件的链接.,为什么要用gcc,随着Linux的GUI改进,也出现了越来越多的IDE开发环境.象VC+,自动完成各个开发流程但这一些IDE基本上是基于gcc编译而且大部分项目,包括嵌入式开发,都是提供gcc命令行开发模式.因此用gcc开发是Linux和嵌入式开发的必须使用的工具.也是基本功之一,hello 编译过程分析,以下将上述gcc编译过程 ,分成几个步骤单独进行 ,并生成每步运行结果供观察第一步是进行预编译,使用-E参数可以让GCC在预处理结束后停止编

6、译过程:# gcc -E hello.c -o hello.i下一步是将hello.i编译为目标代码,这可以通过使用-c参数来完成 , 。 # gcc -c hello.i -o hello.o最后一步是将生成的目标文件链接成可执行文件 # gcc hello.o -o hello注意:gcc编译时是对输入文件扩展名是敏感的,即.c一定会当做C代码编译,.cpp,.C 一定会当成C+代码编译,这一点跟大部分Linux程序不一样gcc的结果输出是后缀名不相关的.只与输出参数相关.这跟一般Linux程序是一样gcc hello.c -o hello.o#虽然后缀名是.o ,但实际是一个应用程序,g

7、cc 各个编译步骤,多文件gcc 的处理,在采用模块化的设计思想进行软件开发时,通常整个程序是由多个源文件组成的,相应地也就形成了多个编译单元,使用GCC能够很好地管理这些编译单元。假设有一个由foo1.c和foo2.c两个源文件组成的程序,为了对它们进行编译,并最终生成可执行程序foo,可以使用下面这条命令: gcc foo1.c foo2.c -o foo 在编译一个包含许多源文件的工程时,若只用一条GCC命令来完成编译是非常浪费时间的。假设项目中有100个源文件需要编译,并且每个源文件中都包含10000行代码,如果像上面那样仅用一条GCC命令来完成编译工作,那么GCC需要将每个源文件都重

8、新编译一遍,然后再全部连接起来。很显然,这样浪费的时间相当多,尤其是当用户只是修改了其中某一个文件的时候,完全没有必要将每个文件都重新编译一遍,因为很多已经生成的目标文件是不会改变的。要解决这个问题,关键是要灵活运用GCC,同时还要借助像Make这样的工具。,用gcc构造程序(1),在只有一个源代码文件构造出来的可执行程序.只需要用到如下形式gcc hello.c -o hello 表示将hello.c一次做完四步,构造出可执行程序 hello , gcc hello.c 将hello.c构造一个可执行程序,有缺省名a.out,但不建议这样做.gcc -c hello.c -o hello这一

9、步骤是初学者常犯错误.以为等于在一次性构造应用程序hello但实际上这只是在编译c并生成一个目标文件hello,即便是没有.o的后缀.这个用file命令可以很容易查看,这个hello是无法执行.,用gcc构造程序(2),使用多个源码的项目,如项目中包含2个以上的源代码,一般要先将源代码编译成目标代码.最后一次链接成可执行程序以链表测试程序为例.整个项目由两个c代码(test_link.c和link_list.c)和一个头文件(link_list.h)组成.头文件是包含在源代码里,由预处理程序处理,不需要编译首先各自己编译成目标文件gcc -c link_list.c #将link_list.c

10、编译成link_list.ogcc -c test_link.c #将test_link.c编译成test_link.o然后将各个目标文件链接成一个文件gcc link_list.o test_link.o -o test_link #生成可执行文件test_link也可以直接把两个文件在一句里编译,但强烈建议不要这样做gcc link_list.c test_link.c -o test_link,编译test_link实例,用gcc构造程序(3),gcc的对绝大部分参数顺序一般没有要求,但为可读性强,最好要按一定顺序执行gcc link_list.o -o test_link.o test

11、_link 等效于gcc link_list.o test_link.o -o test_link ,但前者可读性差很多.但多个-l(链接)参数的顺序是有要求的对于有头文件在多个目录时,需要在编译时多次使用用-I参数加入头文件所在目录.例如test_link.c需要用到 /usr/include, 当前目录下,/home/hxy目录下的头文件.则如下编译gcc -I. -I/usr/include -I/home/hxy -c test_link.c,gcc 构造复杂程序,一个大型项目,一个可执行程序可能拥有多个位于不同目录的头文件,多个源码文件,还可能链接一些静态库或动态库,这一些都需要用到

12、gcc的一些扩展选项.gcc的参数参见下一节可能调用gcc很多次,如果完全手工编写,将是一个浩大的工程需要写一个类似Shell脚本的Makefile来调用gcc构造,GCC选项,gcc 完整使用格式,gcc 使用格式 gcc option | filename . g+ option | filename . 其中 option 为 gcc 使用时的选项(后面会再详), 而 filename 为欲以 gcc 处理的文件 总体选项(Overall Option) -c -S -E -o file -pipe -v -x language,gcc 选项(1),-x language 明确指出后面输

13、入文件的语言为language (而不是从文件名后缀得到的默认选择).这个选项应用于后面 所有的输入文件,直到遇着下一个-x选项. language的可选值有c, objective-c, c-header, c+, cpp-output, assembler,和assembler-with-cpp. gcc -x c+ hello.c强制用c+来编译-x none 关闭任何对语种的明确说明,因此依据文件名后缀处理后面的文件(就象是从未使用过-x选项).,gcc 选项(2),-c 编译或汇编源文件,但是不作连接.编译器输出对应于源文件的目标文件.缺省情况下, GCC通过用.o替换源文件名后缀.

14、c, .i, .s,等等,产生目标文件名.可以使用-o选项选择其他名字.GCC忽略-c选项后面任何无法识别的输入文件(他们不需要编译或汇编).gcc c hello.c,gcc 选项(3),-S编译后即停止,不进行汇编.对于每个输入的非汇编语言文件,输出文件是汇编语言文件. 缺省情况下, GCC通过用.o替换源文件名后缀.c, .i,等等,产生 目标文件名.可以使用-o选项选择其他名字. GCC忽略任何不需要编译的输入文件.相当于编译源码,只生汇编代码 gcc S hello.c o hello.s,gcc 选项(4),-E 预处理后即停止,不进行编译.预处理后的代码送往标准输出. GCC忽略

15、任何不需要预处理的输入文件gcc E hello.c o hello.i-v (在标准错误)显示执行编译阶段的命令.同时显示编译器驱动程序,预处理器,编译器的版本号.gcc -v,gcc 选项(5),-o file指定输出文件为file.该选项不在乎GCC产生什么输出,无论是可执行文件,目标文件,汇编文件还是 预处理后的C代码. 由编译阶段决定,输入的格式gcc -E hello.c -o hello.igcc -c hello.i -o hello.ogcc hello.c o hello只能输出一个文件,gcc 选项(6),-pipe 在编译过程的不同阶段间使用管道而非临时文件进行通信.在

16、将源代码变成可执行文件的过程中,需要经过许多中间步骤,包含预处理、编译、汇编和连接。这些过程实际上是由不同的程序负责完成的。大多数情况下GCC可以为Linux程序员完成所有的后台工作,自动调用相应程序进行处理。 是GCC在处理每一个源文件时,最终都需要生成好几个临时文件才能完成相应的工作,从而无形中导致处理速度变慢。例如,GCC在处理一个源文件时,可能需要一个临时文件来保存预处理的输出、一个临时文件来保存编译器的输出、一个临时文件来保存汇编器的输出,而读写这些临时文件显然需要耗费一定的时间。当软件项目变得非常庞大的时候,花费在这上面的代价可能会变得很沉重。 解决的办法是,使用Linux提供的一

17、种更加高效的通信方式管道。它可以用来同时连接两个程序,其中一个程序的输出将被直接作为另一个程序的输入,这样就可以避免使用临时文件,但编译时却需要消耗更多的内存。 gcc -pipe foo.c -o foo,关于宏(macro)的选项,-Dmacro 定义宏macro,宏的内容定义为字符串1.gcc test_m.c D_DEBUG o test_m-Dmacro=defn 定义宏macro的内容为defn.命令行上所有的-D选项在 -U选项之前处理. gcc test_m.c D_DBG_NAME=hello o test_m-Umacro 取消宏macro. -U选项在所有的-D选项之后处

18、理,但是优先于任何 -include,gcc警告提示功能,GCC包含完整的出错检查和警告提示功能,它们可以帮助Linux程序员写出更加专业和优美的代码。编译警告代码-pedantic打开完全服从ANSI C标准所需的全部警告诊断,如里出现非标准扩展,则拒绝编译,所以叫书呆子pedant.-ansi 支持符合ANSI标准的C程序.这样就会关闭GNU C中某些不兼容ANSI C的特性,例如asm, inline和 typeof关键字,以及诸如unix和vax这些表明当前系统类型的预定义宏.同时开启 不受欢迎和极少使用的ANSI trigraph特性,以及禁止$成为标识符的一部分.与pedantic

19、区别在于,只是警告,如果需要停止编译,仍然需要打开-pedantic,gcc警告提示功能(2),-Wall 打开所有编译警告gcc -Wall illcode.c -o illcode -Werror 视警告为错误;出现任何警告即放弃编译. gcc -Wall -Werror illcode.c -o illcode-w禁止输出警告信息,调试分析选项,-g以操作系统的本地格式(stabs, COFF, XCOFF,或DWARF).产生调试信息. GDB能够使用这些调试信息,是进行gdb调试必备条件和大多数C编译器不同, GNU CC允许结合使用-g和-O选项,但一般不建议一起使用gcc hel

20、lo.c -g -o hello-pg 产生额外代码,用于输出profile信息,供分析程序gprof使用. 所有调试选项会使用最终输出文件尺寸急剧增加,在最后发布,需要使用strip 命令把调试信息去掉,strip hello,gcc使用库,使用第三方库,在Linux下开发软件时,完全不使用第三方函数库的情况是比较少见的,通常来讲都需要借助一个或多个函数库的支持才能够完成相应的功能。从程序员的角度看,函数库实际上就是一些头文件(.h)和库文件(.so或者.a)的集合。虽然Linux下的大多数函数都默认将头文件放到/usr/include/目录下,而库文件则放到/usr/lib/目录下,但并不

21、是所有的情况都是这样。正因如此,GCC在编译时必须有自己的办法来查找所需要的头文件和库文件。GCC采用搜索目录的办法来查找所需要的文件,-I选项可以向GCC的头文件搜索路径中添加新的目录。例如,如果在/home/hxy/upgrade/include/目录下有编译时所需要的头文件,为了让GCC能够顺利地找到它们,就可以使用-I选项: gcc foo.c -I /home/xiaowp/include -o foo 在一个gcc命令中可以用多个 -I,两大类库形式,C/C+可以使用两种库.一种是静态库,另外一种是动态库.静态库在链接时会把库目标代码与最终的可执行程序一起链接到一个文件,这样相对尺

22、寸较大.但处理简单.而动态库是可执行程序在运行,动态加载到进程内存中去.动态库与可执行程序是分离的两部分文件.两者在作用是完全等效,主要是使用方法不同.由开发者根据项目情况自行评估使用哪种形式.Windows下的静态库是以 lib为后缀名的文件,而动态库是以DLL为后缀名的文件.Linux下的动态链接库是so为后缀,和静态链接库以.a为后缀名,Linux的库的命名,Linux库的命名有一个特殊的要求,即要是lib打头,以.so或a结尾libc.so #标准C库,动态链接库libpthread.a #线程库,的静态链接库版本.在一般使用时,为防止不同版本库互相覆盖,一般还在系统库名后加入版本号.

23、libm.so.6 #math库 ver 6.0版本Libc-2.3.2.so #标准C ver 2.3.2动态库但为方便gcc 使用,通常都对这一些带版本名的库作一个符号链接,链接名则是标准形式,如libc.so.Linux一般把系统库放在 /lib下.这是大部分库命名的习惯,也可以不遵守,如果强行做一个叫mystd.a 的库,但使用起来很不方便,如不能使用-l参数等,所以建议不要这样做.,gcc链接库,gcc是在链接时,把库加入可以程序中.一个特殊静态链接库方式.把库完整名字加入gcc -o hello hello.o libmy.a 链接hello.o,和库libmy.a到某一个程序he

24、llo中gcc -o hello hello.o libmy.so链接hello.o,和库libmy.so到某一个程序hello中,注意这里没有直接把libmy.so代码加入hello中这一方法主要用于链接不标准库名称,或混和链接(即一部分库用于静态版本,一部分库用动态版本).但不是正规用法,强烈建议不要使用这一方法,gcc -l 参数的使用,gcc -l参数 用来链接库标准表达式方式.-l接的库名,是去掉lib和后缀名(.so,.a)剩下的部分,gcc foo.c -lpthread -o foo 构造foo,链接库pthread . -lpthread 表示 链接 libpthread.s

25、o 由于去掉后缀名,gcc -l如何知道链接是动态库还是静态库?,gcc有如下规则:如果gcc所能找到库目录同时有两种版本,优先链接动态链接库版本如果gcc所能找到库目录只有静态版本,则采用静态版本如果加上 -static 参数,gcc 则强制链接静态版本gcc foo.c -static -lpthread -o foo -lpthread 表示 链接 libpthread.a,一个使用线程库的例子,gcc -L 参数的使用,gcc所编译的目标文件和库通常不是在同一个目录下.因此必须强制指明gcc要从哪一个目录加载库gcc 在链接时采用 -L参数来指明从哪一个目录加载库例如,如果在/home

26、/hxy/lib/目录下有链接时所需要的库文件libfoo.sogcc foo.c -L/home/hxy/lib -lfoo -o foo一个gcc语句可以包含多个-L参数在编译时目标文件时使用-L无效标准库,gcc能自行找到,无需使用-L参数在一些应用中,链接多个库是有顺序的,大部分无所谓如在系统中 liba.a 使用libpthread.a中的函数,而可执行程序同时使用两个库,则使用者liba.a的链接语句放在被使用者libpthread.a的前面, gcc foo.c -L/home/hxy/lib la -lpthread -o foo只是为了保险.在BerkeleyDB中就出现这种

27、情况,pthread在前,则所有线程库的函数找不到,gcc -I 参数的使用,库的头文件通常也跟源文件不在同一个目录之下,为了让gcc找到头文件,可以-I来加入头目录.gcc是在编译时使用-I,在链接时无效.这跟-L参数刚好相反一个gcc语句中可以使用多个-I,gcc创建库,关于库的演示代码,在随后的演示中,将采用如下演示代码.Strlen.c,Strnlen.c 分别实现了两个自定义示字符串长度函数这两个函数的声明在String.h中main.c分别用静态链接,隐式动态链接来测试使用了Strlen.c ,Strnlen.c的函数,并使用.main_dl.c 用显示动态链接来调用库代码在随后的

28、例子里把Strlen.c ,Strnlen.c 分别编译成静态库.libstr.a,隐式动态库 libstr.so,显示动态库libstrdl.so,库名中最好不用出现大写字母,gcc 是按小写字母来查找的,关于静态链接库,在LINUX下,静态函数库是以.a作后缀的 ,类似于Windows 的lib在链接后,静态库的函数都会链接到最终的可执行程序里.这样可执行程序的尺寸比动态链接要大.静态链接的好处是不需要外部文件的支持,独立运行.在嵌入式环境下,如果尺寸影响不大,最好用静态编译.,创建静态链接库,gcc不能直接创建静态库.必须要用归档命令ar来创建ar用于建立、修改、提取档案文件(archi

29、ve)。archive是一个包含多个被包含文件的单一文件(也称之为库文件),其结构保证了可以从中检索并得到原始的被包含文件(称之为archive中的member)。ar可以把任何文件归在一起,但通常是用来把gcc编译的目标文件(.o),合在一个静态库中静态库创建$ gcc -Wall -c file1.c file2.c file3.c #一次性编译三个.o$ ar rv libNAME.a file1.o file2.o file3.o #把三个o合在一起,ar 参数,dmpqrtx中的操作选项在命令中只能并且必须使用其中一个,它们的含义如下:d:从库中删除模块。按模块原来的文件名指定要删除

30、的模块。如果使用了任选项v则列出被删除的每个模块。 m:该操作是在一个库中移动成员。当库中如果有若干模块有相同的符号定义(如函数定义),则成员的位置顺序很重要。如果没有指定任选项,任何指定的成员将移到库的最后。也可以使用a,b,或I任选项移动到指定的位置。 p:显示库中指定的成员到标准输出。如果指定任选项v,则在输出成员的内容前,将显示成员的名字。如果没有指定成员的名字,所有库中的文件将显示出来。 q:快速追加。增加新模块到库的结尾处。并不检查是否需要替换。a,b,或I任选项对此操作没有影响,模块总是追加的库的结尾处。如果使用了任选项v则列出每个模块。 这时,库的符号表没有更新,可以用ar s

31、或ranlib来更新库的符号表索引。,ar 参数(2),r:在库中插入模块(替换)。当插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar显示一个错误消息,并不替换其他同名模块。默认的情况下,新的成员增加在库的结尾处,可以使用其他任选项来改变增加的位置。 t:显示库的模块表清单。一般只显示模块名。 x:从库中提取一个成员。如果不指定要提取的模块,则提取库中所有的模块。但实际最多参数只有ar rv . Replace +verbosear rcv . Replace +Create + verbose,创建一个静态库的脚本,gcc -c Strlen.cgcc

32、 -c Strnlen.c#把两个目标文件装入静态库libstr.a中ar rv libstr.a Strlen.o Strnlen.o gcc -c main.c -I.#链接main_a时,一并链接libstr.a#gcc main.o libmy.a -o main_a #非标准用法,用全名gcc main.o -L. -lstr -o main_a #标准用法,动态链接库的创建,动态链接库的创建分为两步:1.编译目标文件,必须带上-fpic 标志,使输出的对象模块是按照可重定位地址方式生成的 gcc -c Strlen.c -fpic gcc -c Strnlen.c -fpic 2.

33、将加入动态库的目标文件合并在一起,必须带上-shared ,明确表示是动态链接库gcc -shared Strlen.o Strnlen.o -o libstr.so两步可以合并成一步,但一般不建议这样做gcc -fpic -shared Strlen.c Strnlen.c -o libstr.soso是Shared Object 的缩写,动态链接库的创建,#gcc -fpic -shared Strlen.c Strnlen.c -o libstr.so gcc -c -fpic Strlen.cgcc -c -fpic Strnlen.c#生成动态链接库gcc -shared Strle

34、n.o Strnlen.o -o libstr.so#编译测试程序gcc -c main.c -I.#链接主程序和动态库#gcc main.o libstr.so -o main_so #非标准链接方式gcc main.o -L. -lstr -o main_so,运行中使用动态链接库,一个使用动态链接库的程序运行时,要做一下设置.否则应用程序会报找不到动态库的错误隐式调用和显式调用两种调用方法.隐式调用是不采用特殊系统调用,只是在gcc链接采用-l,-L链接.这样代码影响不大显式调用是在运行时,使用动态库的文件名来加载,具有灵活的特点,缺点就是必须使用特定的,不可移植的系统调用来编写.过程比

35、较复杂一个程序运行后,可以用命令ldd来检查它使用了哪一些动态库ldd ./hello,隐式调用动态方法,必须要能让程序运行能找动态链接库,Linux有如下几种方法.把库所在路径加入/etc/ld.so.conf,程序加载是会首先到这里路径查找设置环境变量LD_LIBRARY_PATH,把库所在路径加入这个变量中,这是最常用的方法演示代码将采用这一方法运行,写一个脚本run_so.sh将export LD_LIBRARY_PATH和执行语句写在一行命令中,并用;隔开export LD_LIBRARY_PATH=$PWD;./mani_so,#!/bin/shexport LD_LIBRARY_

36、PATH=$PWD:$LD_LIBRARY_PATH./main_soldd ./main_so #用ldd查看用了哪些动态库,隐式动态库的执行结果,动态库的显示调用,显示调用, Linux 提供 4 个库函数( dlopen , dlerror , dlsym 和 dlclose ),一个 include 文件( dlfcn.h )以支持动态链接装入器 dlopen 将共享目标文件打开并且映射到内存中,并且返回句柄 dlsym返回一个指向被请求入口点的指针 dlerror 返回 NULL 或者一个指向描述最近错误的 ASCII 字符串的指针 dlclose关闭句柄并且取消共享目标文件的映射

37、这四个函数被包含在libdl.a/libdl.so之中,所以使用动态显示调用,必须要链接-ldl这一机制是后来加入,模仿Windows下的LoadLibrary/FreeLibrary机制不同调用方式对动态库本身没有任何影响,需要调整是调用库函数的应用程序,要做较大调整.,dlopen()的说明,void *dlopen(const char *filename, int flag);将共享目标文件打开并且映射到内存中,并且返回句柄 声明在头文件dlfcn.h之中第一个参数:指定共享库的名称,将会在下面位置查找指定的共享库。 dlopen call 中的绝对文件路径 在 LD_LIBRARY_

38、PATH 环境变量中指定的目录中 在 /etc/ld.so.cache 中指定的库列表之中 先在 /usr/lib 之中,然后在 /lib 之中 第二个参数:指定如何打开共享库。RTLD_NOW:将共享库中的所有函数加载到内存RTLD_LAZY:会推后共享库中的函数的加载操作,直到调用dlsym()时方加载某函数,其它dl函数声明,const char *dlerror(void);如果上一步的dl函数出错,则返回非空字符串,指明错误原因 void *dlsym(void *handle, char *symbol);用库函数名字symbol在dlopen打开的handle查找一个函数指针,如

39、果找到,则放入返回值中,应用程序可以直接用函数指针来操作.int dlclose(void *handle);关闭dlopen打开的句柄,关于使用显示动态库的步骤,必须包含头文件 #include声明使用的到库函数的函数指针用dlopen打开共享库用dlsym取得对应库函数的函数指针如果成功,使用直接调用这个指针.相当于使用库函数用完共享库后,用dlclose释放共享库,使用显示调用实例(1),#include#include/* 加入头文件支持*/int main(int argc, char* argv) /define function pointor int (*pStrlenFun)

40、(char* pStr); /声明对应的函数的函数指针 int (*pStrnlenFun)(char* pStr, int ulMaxLen); char str = hello world; unsigned long ulLength = 0; void *pdlHandle; char *pszErr; pdlHandle = dlopen(“./libstrdl.so”, RTLD_LAZY); /加载动态链接库/libstrdl.so if(!pdlHandle) printf(Failed load libraryn); ,使用显示调用实例(2),/* 接上一段代码 */ /*

41、判断dlopen是否打开出错,如是,打印错误后退出*/ pszErr = dlerror(); if(pszErr != NULL) printf(%sn, pszErr); return 0; /从库中查找库函数Strlen的函数指针 pStrlenFun = dlsym(pdlHandle, Strlen); /获取函数的地址 pszErr = dlerror(); if(pszErr != NULL) printf(%sn, pszErr); return 0; ,使用显示调用实例(3),/* 接上一段代码 */ /*从库中查找库函数Strnlen的函数指针 */ pStrnlenFun

42、 = dlsym(pdlHandle, StrNlen); pszErr = dlerror(); if(pszErr != NULL) printf(%sn, pszErr); return 0; printf(The string is : %sn, str); ulLength = pStrlenFun(str); /直接使用函数指针,相当于调用库函数 printf(The string length is : %d(use Strlen)n, ulLength); ulLength = pStrnlenFun(str, 10); printf(The string length is

43、: %d(use StrNlen)n, ulLength); dlclose(pdlHandle); /关闭共享库 return 0;,创建显示调用动态库,rm -f libstrdl.sorm -f *.o main#gcc -fpic -shared Strlen.c Strnlen.c -o libstr.so gcc -c -fpic Strlen.cgcc -c -fpic Strnlen.c#创建动态链接库gcc -shared Strlen.o Strnlen.o -o libstrdl.so gcc -c main_dl.c -I.#必须同时加入strdl和dl库支持gcc m

44、ain_dl.o -L. -lstrdl -ldl -o main_dl,运行显示调用实例,代码优化,Gcc代码优化(1),代码优化指的是编译器通过分析源代码,找出其中尚未达到最优的部分,然后对其重新进行组合,目的是改善程序的执行性能。GCC提供的代码优化功能非常强大,它通过编译选项-On来控制优化代码的生成,其中n是一个代表优化级别的整数。对于不同版本的GCC来讲,n的取值范围及其对应的优化效果可能并不完全相同,比较典型的范围是从0变化到2或3。 编译时使用选项-O可以告诉GCC同时减小代码的长度和执行时间,其效果等价于-O1。在这一级别上能够进行的优化类型虽然取决于目标处理器,但一般都会包

45、括线程跳转(Thread Jump)和延迟退栈(Deferred Stack Pops)两种优化。选项-O2告诉GCC除了完成所有-O1级别的优化之外,同时还要进行一些额外的调整工作,如处理器指令调度等。选项-O3则除了完成所有-O2级别的优化之外,还包括循环展开和其它一些与处理器特性相关的优化工作。通常来说,数字越大优化的等级越高,同时也就意味着程序的运行速度越快。许多Linux程序员都喜欢使用-O2选项,因为它在优化长度、编译时间和代码大小之间,取得了一个比较理想的平衡点。,Gcc代码优化(2),不带优化 gcc -Wall optimize.c -o optimize 做了优化gcc -

46、Wall -O optimize.c -o optimize time借助Linux提供的time命令,可以大致统计出该程序在运行时所需要的时间,比较两次时间 # time ./optimize,代码优化实例,一个做了大量浮点数除法的程序.,#include int main(void) double counter; double result; double temp; for ( counter = 0; counter 2000.0 * 2000.0 * 2000.0 / 20.0 + 2020; counter += (5 - 1) / 4) temp = counter / 197

47、9; result = counter; printf(Result is %lfn, result); return 0; ,代码优化的结果比较,Gcc代码优化(3),-O -O1-O2 多优化一些.除了涉及空间和速度交换的优化选项,执行几乎所有的优化工作.例如不进行循环展开(loop unrolling)和函数内嵌(inlining).和-O选项比较,这个选项既增加了编译时间,也提高了生成代码的 运行效果.-O3 优化的更多.除了打开-O2所做的一切,它还打开了-finline-functions选项. -O0 不优化.如果指定了多个-O选项,不管带不带数字,最后一个选项才是生效的选项.,Gcc代码优化(4),避免优化代码场合:程序开发的时候 优化等级越高,消耗在编译上的时间就越长,因此在开发的时候最好不要使用优化选项,只有到软件发行或开发结束的时候,才考虑对最终生成的代码进行优化。 资源受限的时候 一些优化选项会增加可执行代码的体积,如果程序在运行时能够申请到的内存资源非常紧张(如一些实时嵌入式设备),那就不要对代码进行优化,因为由这带来的负面影响可能会产生非常严重的后果。 跟踪调试的时候 在对代码进行优化的时候,某些代码可能会被删除或改写,或者为了取得更佳的性能而进行重组,从而使跟踪和调试变得异常困难。,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 教育学

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报