您当前所在位置:首页 > PPT课件 > 工作PPT > 培训教程PPT → 7_GNU GCC 开发教程ppt

TT手机客户端登录:7_GNU GCC 开发教程ppt

PPT预览

课件下载 www.sixtyyearswar.com 7_GNU GCC 开发教程ppt

PPT内容

这是一个关于7_GNU GCC 开发教程ppt,主要介绍了gnu gcc基本信息;gcc 支持的文件格式;GCC 组成;gcc的起步;Hello程序;用GCC编译程序;GCC选项;gcc使用库;gcc创建库;代码优化;交叉开发GCC等内容,欢迎点击下载!

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 支持的文件格式
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/bin
gcc 是一组编译工具的总称,包含如下工作
C 编译器 cc,cc1,cc1plus,gcc
C++编译器 c++,cc1plus,g++
源码预处理程序 cpp,cpp0
库文件 libgcc.a,libgcc_eh.a,libgcc_s.so,ibiberty.a,libstdc++,libsupc++.a
gcc的起步
生成一个hello,world程序
gcc hello.c -o hello #把hello.c编译成一个可执行程序 hello
gcc hello.c   #不指定输出名,生成一个a.out
Hello程序
编译hello
可执行程序的构造
任何一个可执行程序从源代码到可执行的二进制程序之中都要经过固定的几步
预编译 (Pre-Processing)
这一步完成对预编译代码的处理
编译(Compiling)
将源代码编译成汇编代码
汇编(Assembling)
将汇编代码汇编成目标文件
链接(Linking)
将目标代码和所需要库的链成一个完整的应用程序
集成开发环境(IDE)自动协助开发完成这几步,如VC++
在Linux 下,如果使用命令行开发工具(gcc,ld,ar)等,需要用户手工调用这一些命令来完成这几步骤.
gcc在构建程序的作用
gcc在构建应用程序里,会调用不同的应用程序完成每一步.因此在开发中,gcc处于一个核心地位.大部分开发只需要调用gcc即可
gcc所做操作
Gcc 调用cpp进行预处理
Gcc 调用cc1进行编译,会生成汇编代码
Gcc 调用as 对汇编代码,生成扩展名为.o的目标文件
Gcc 调用ld 来完成对所有目标文件的链接.
为什么要用gcc
随着Linux的GUI改进,也出现了越来越多的IDE开发环境.象VC++,自动完成各个开发流程
但这一些IDE基本上是基于gcc编译
而且大部分项目,包括嵌入式开发,都是提供gcc命令行开发模式.
因此用gcc开发是Linux和嵌入式开发的必须使用的工具.也是基本功之一
hello 编译过程分析
以下将上述gcc编译过程 ,分成几个步骤单独进行 ,并生成每步运行结果供观察
第一步是进行预编译,使用-E参数可以让GCC在预处理结束后停止编译过程:
#  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 ,但实际是一个应用程序
gcc 各个编译步骤
多文件gcc 的处理
在采用??榛纳杓扑枷虢腥砑⑹?,通常整个程序是由多个源文件组成的,相应地也就形成了多个编译单元,使用GCC能够很好地管理这些编译单元。假设有一个由foo1.c和foo2.c两个源文件组成的程序,为了对它们进行编译,并最终生成可执行程序foo,可以使用下面这条命令:
gcc foo1.c foo2.c -o foo
在编译一个包含许多源文件的工程时,若只用一条GCC命令来完成编译是非常浪费时间的。假设项目中有100个源文件需要编译,并且每个源文件中都包含10000行代码,如果像上面那样仅用一条GCC命令来完成编译工作,那么GCC需要将每个源文件都重新编译一遍,然后再全部连接起来。很显然,这样浪费的时间相当多,尤其是当用户只是修改了其中某一个文件的时候,完全没有必要将每个文件都重新编译一遍,因为很多已经生成的目标文件是不会改变的。要解决这个问题,关键是要灵活运用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
这一步骤是初学者常犯错误.以为等于在一次性构造应用程序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编译成link_list.o
gcc -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_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 构造复杂程序
一个大型项目,一个可执行程序可能拥有多个位于不同目录的头文件,多个源码文件,还可能链接一些静态库或动态库,这一些都需要用到gcc的一些扩展选项.
gcc的参数参见下一节
可能调用gcc很多次,如果完全手工编写,将是一个浩大的工程
需要写一个类似Shell脚本的Makefile来调用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
明确指出后面输入文件的语言为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'替换源文件名后缀`.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忽略任何不需要预处理的输入文件
gcc –E hello.c –o hello.i
-v
(在标准错误)显示执行编译阶段的命令.同时显示编译器驱动程序,预处理器,编译器的版本号.
gcc -v
gcc 选项(5)
-o file
指定输出文件为file.该选项不在乎GCC产生什么输出,无论是可执行文件,目标文件,汇编文件还是 预处理后的C代码.
由编译阶段决定,输入的格式
gcc -E hello.c -o hello.i
gcc -c hello.i -o hello.o
gcc hello.c –o hello
只能输出一个文件
gcc 选项(6)
-pipe
在编译过程的不同阶段间使用管道而非临时文件进行通信.
在将源代码变成可执行文件的过程中,需要经过许多中间步骤,包含预处理、编译、汇编和连接。这些过程实际上是由不同的程序负责完成的。大多数情况下GCC可以为Linux程序员完成所有的后台工作,自动调用相应程序进行处理。
是GCC在处理每一个源文件时,最终都需要生成好几个临时文件才能完成相应的工作,从而无形中导致处理速度变慢。例如,GCC在处理一个源文件时,可能需要一个临时文件来保存预处理的输出、一个临时文件来保存编译器的输出、一个临时文件来保存汇编器的输出,而读写这些临时文件显然需要耗费一定的时间。当软件项目变得非常庞大的时候,花费在这上面的代价可能会变得很沉重。
解决的办法是,使用Linux提供的一种更加高效的通信方式—管道。它可以用来同时连接两个程序,其中一个程序的输出将被直接作为另一个程序的输入,这样就可以避免使用临时文件,但编译时却需要消耗更多的内存。
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'选项之后处理,但是优先于任何 `-include'
gcc警告提示功能
GCC包含完整的出错检查和警告提示功能,它们可以帮助Linux程序员写出更加专业和优美的代码。
编译警告代码
-pedantic
打开完全服从ANSI C标准所需的全部警告诊断,如里出现非标准扩展,则拒绝编译,所以叫书呆子pedant.
-ansi
支持符合ANSI标准的C程序.
这样就会关闭GNU C中某些不兼容ANSI C的特性,例如asm, inline和 typeof关键字,以及诸如unix和vax这些表明当前系统类型的预定义宏.同时开启 不受欢迎和极少使用的ANSI trigraph特性,以及禁止`$'成为标识符的一部分.
与pedantic区别在于,只是警告,如果需要停止编译,仍然需要打开-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 hello.c -g -o hello
-pg
产生额外代码,用于输出profile信息,供分析程序gprof使用.
所有调试选项会使用最终输出文件尺寸急剧增加,在最后发布,需要使用strip 命令把调试信息去掉,
strip  hello
使用第三方库
在Linux下开发软件时,完全不使用第三方函数库的情况是比较少见的,通常来讲都需要借助一个或多个函数库的支持才能够完成相应的功能。
从程序员的角度看,函数库实际上就是一些头文件(.h)和库文件(.so或者.a)的集合。虽然Linux下的大多数函数都默认将头文件放到/usr/include/目录下,而库文件则放到/usr/lib/目录下,但并不是所有的情况都是这样。正因如此,GCC在编译时必须有自己的办法来查找所需要的头文件和库文件。
GCC采用搜索目录的办法来查找所需要的文件,-I选项可以向GCC的头文件搜索路径中添加新的目录。例如,如果在/home/hxy/upgrade/include/目录下有编译时所需要的头文件,为了让GCC能够顺利地找到它们,就可以使用-I选项:
gcc foo.c -I /home/xiaowp/include -o foo
在一个gcc命令中可以用多个 -I
两大类库形式
C/C++可以使用两种库.一种是静态库,另外一种是动态库.
静态库在链接时会把库目标代码与最终的可执行程序一起链接到一个文件,这样相对尺寸较大.但处理简单.
而动态库是可执行程序在运行,动态加载到进程内存中去.动态库与可执行程序是分离的两部分文件.
两者在作用是完全等效,主要是使用方法不同.由开发者根据项目情况自行评估使用哪种形式.
Windows下的静态库是以 lib为后缀名的文件,而动态库是以DLL为后缀名的文件.
Linux下的动态链接库是so为后缀,和静态链接库以.a为后缀名
Linux的库的命名
Linux库的命名有一个特殊的要求,即要是lib打头,以.so或a结尾
libc.so #标准C库,动态链接库
libpthread.a #线程库,的静态链接库版本.
在一般使用时,为防止不同版本库互相覆盖,一般还在系统库名后加入版本号.
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到某一个程序hello中
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.so
由于去掉后缀名,gcc -l如何知道链接是动态库还是静态库?,gcc有如下规则:
如果gcc所能找到库目录同时有两种版本,优先链接动态链接库版本
如果gcc所能找到库目录只有静态版本,则采用静态版本
如果加上 -static 参数,gcc 则强制链接静态版本
gcc foo.c -static -lpthread  -o foo
-lpthread 表示 链接 libpthread.a
一个使用线程库的例子
gcc -L 参数的使用
gcc所编译的目标文件和库通常不是在同一个目录下.因此必须强制指明gcc要从哪一个目录加载库
gcc 在链接时采用 -L参数来指明从哪一个目录加载库
例如,如果在/home/hxy/lib/目录下有链接时所需要的库文件libfoo.so
gcc 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中就出现这种情况,pthread在前,则所有线程库的函数找不到
gcc -I 参数的使用
库的头文件通常也跟源文件不在同一个目录之下,为了让gcc找到头文件,可以-I<头文件目录>来加入头目录.
gcc是在编译时使用-I,在链接时无效.这跟-L参数刚好相反
一个gcc语句中可以使用多个-I
关于库的演示代码
在随后的演示中,将采用如下演示代码.
Strlen.c,Strnlen.c 分别实现了两个自定义示字符串长度函数
这两个函数的声明在String.h中
main.c分别用静态链接,隐式动态链接来测试使用了Strlen.c ,Strnlen.c的函数,并使用.
main_dl.c 用显示动态链接来调用库代码
在随后的例子里把Strlen.c ,Strnlen.c 分别编译成静态库.libstr.a,隐式动态库 libstr.so,显示动态库libstrdl.so,
库名中最好不用出现大写字母,gcc 是按小写字母来查找的
关于静态链接库
在LINUX下,静态函数库是以.a作后缀的 ,类似于Windows 的lib
在链接后,静态库的函数都会链接到最终的可执行程序里.这样可执行程序的尺寸比动态链接要大.
静态链接的好处是不需要外部文件的支持,独立运行.在嵌入式环境下,如果尺寸影响不大,最好用静态编译.
创建静态链接库
gcc不能直接创建静态库.必须要用归档命令ar来创建
ar用于建立、修改、提取档案文件(archive)。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:从库中删除???。按??樵吹奈募付ㄒ境哪??。如果使用了任选项v则列出被删除的每个???。
m:该操作是在一个库中移动成员。当库中如果有若干??橛邢嗤姆哦ㄒ?如函数定义),则成员的位置顺序很重要。如果没有指定任选项,任何指定的成员将移到库的最后。也可以使用'a','b',或'I'任选项移动到指定的位置。
p:显示库中指定的成员到标准输出。如果指定任选项v,则在输出成员的内容前,将显示成员的名字。如果没有指定成员的名字,所有库中的文件将显示出来。
q:快速追加。增加新??榈娇獾慕嵛泊?。并不检查是否需要替换。'a','b',或'I'任选项对此操作没有影响,??樽苁亲芳拥目獾慕嵛泊?。如果使用了任选项v则列出每个???。 这时,库的符号表没有更新,可以用'ar s'或ranlib来更新库的符号表索引。
ar 参数(2)
r:在库中插入???替换)。当插入的??槊丫诳庵写嬖?,则替换同名的???。如果若干??橹杏幸桓瞿?樵诳庵胁淮嬖?,ar显示一个错误消息,并不替换其他同名???。默认的情况下,新的成员增加在库的结尾处,可以使用其他任选项来改变增加的位置。
t:显示库的??楸砬宓?。一般只显示??槊?。
x:从库中提取一个成员。如果不指定要提取的???,则提取库中所有的???。
但实际最多参数只有
ar rv .... Replace +verbose
ar rcv .... Replace +Create + verbose
创建一个静态库的脚本
动态链接库的创建
动态链接库的创建分为两步:
1.编译目标文件,必须带上-fpic 标志,使输出的对象??槭前凑湛芍囟ㄎ坏刂贩绞缴傻?
gcc -c Strlen.c -fpic
gcc -c Strnlen.c -fpic
2.将加入动态库的目标文件合并在一起,必须带上-shared ,明确表示是动态链接库
gcc -shared Strlen.o Strnlen.o -o libstr.so
两步可以合并成一步,但一般不建议这样做
gcc -fpic -shared Strlen.c Strnlen.c -o libstr.so
so是Shared Object 的缩写
动态链接库的创建
运行中使用动态链接库
一个使用动态链接库的程序运行时,要做一下设置.否则应用程序会报找不到动态库的错误
隐式调用和显式调用两种调用方法.
隐式调用是不采用特殊系统调用,只是在gcc链接采用-l,-L链接.这样代码影响不大
显式调用是在运行时,使用动态库的文件名来加载,具有灵活的特点,缺点就是必须使用特定的,不可移植的系统调用来编写.过程比较复杂
一个程序运行后,可以用命令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
隐式动态库的执行结果
动态库的显示调用
显示调用, Linux 提供 4 个库函数( dlopen , dlerror , dlsym 和 dlclose ),一个 include 文件( dlfcn.h )以支持动态链接装入器
dlopen 将共享目标文件打开并且映射到内存中,并且返回句柄
dlsym返回一个指向被请求入口点的指针
dlerror 返回 NULL 或者一个指向描述最近错误的 ASCII 字符串的指针
dlclose关闭句柄并且取消共享目标文件的映射
这四个函数被包含在libdl.a/libdl.so之中,所以使用动态显示调用,必须要链接-ldl
这一机制是后来加入,模仿Windows下的LoadLibrary/FreeLibrary机制
不同调用方式对动态库本身没有任何影响,需要调整是调用库函数的应用程序,要做较大调整.
dlopen()的说明
void *dlopen(const char *filename, int flag);
将共享目标文件打开并且映射到内存中,并且返回句柄
声明在头文件dlfcn.h之中
第一个参数:指定共享库的名称,将会在下面位置查找指定的共享库。
dlopen call 中的绝对文件路径
在 LD_LIBRARY_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查找一个函数指针,如果找到,则放入返回值中,应用程序可以直接用函数指针来操作.
int dlclose(void *handle);
关闭dlopen打开的句柄
关于使用显示动态库的步骤
必须包含头文件 #include<dlfcn.h>
声明使用的到库函数的函数指针
用dlopen打开共享库
用dlsym取得对应库函数的函数指针
如果成功,使用直接调用这个指针.相当于使用库函数
用完共享库后,用dlclose释放共享库
使用显示调用实例(1)
使用显示调用实例(2)
使用显示调用实例(3)
创建显示调用动态库
运行显示调用实例
Gcc代码优化(1)
代码优化指的是编译器通过分析源代码,找出其中尚未达到最优的部分,然后对其重新进行组合,目的是改善程序的执行性能。GCC提供的代码优化功能非常强大,它通过编译选项-On来控制优化代码的生成,其中n是一个代表优化级别的整数。对于不同版本的GCC来讲,n的取值范围及其对应的优化效果可能并不完全相同,比较典型的范围是从0变化到2或3。
编译时使用选项-O可以告诉GCC同时减小代码的长度和执行时间,其效果等价于-O1。在这一级别上能够进行的优化类型虽然取决于目标处理器,但一般都会包括线程跳转(Thread Jump)和延迟退栈(Deferred Stack Pops)两种优化。选项-O2告诉GCC除了完成所有-O1级别的优化之外,同时还要进行一些额外的调整工作,如处理器指令调度等。选项-O3则除了完成所有-O2级别的优化之外,还包括循环展开和其它一些与处理器特性相关的优化工作。通常来说,数字越大优化的等级越高,同时也就意味着程序的运行速度越快。许多Linux程序员都喜欢使用-O2选项,因为它在优化长度、编译时间和代码大小之间,取得了一个比较理想的平衡点。
Gcc代码优化(2)
不带优化
gcc -Wall optimize.c -o optimize
做了优化
gcc -Wall -O optimize.c -o optimize
time
借助Linux提供的time命令,可以大致统计出该程序在运行时所需要的时间,比较两次时间
# time ./optimize
代码优化实例
一个做了大量浮点数除法的程序.
代码优化的结果比较
Gcc代码优化(3)
-O -O1
-O2
多优化一些.除了涉及空间和速度交换的优化选项,执行几乎所有的优化工作.例如不进行循环展开(loop unrolling)和函数内嵌(inlining).和-O选项比较,这个选项既增加了编译时间,也提高了生成代码的 运行效果.
-O3
优化的更多.除了打开-O2所做的一切,它还打开了-finline-functions选项.
-O0 不优化.
如果指定了多个-O选项,不管带不带数字,最后一个选项才是生效的选项.
Gcc代码优化(4)
避免优化代码场合:
程序开发的时候 优化等级越高,消耗在编译上的时间就越长,因此在开发的时候最好不要使用优化选项,只有到软件发行或开发结束的时候,才考虑对最终生成的代码进行优化。
资源受限的时候 一些优化选项会增加可执行代码的体积,如果程序在运行时能够申请到的内存资源非常紧张(如一些实时嵌入式设备),那就不要对代码进行优化,因为由这带来的负面影响可能会产生非常严重的后果。
跟踪调试的时候 在对代码进行优化的时候,某些代码可能会被删除或改写,或者为了取得更佳的性能而进行重组,从而使跟踪和调试变得异常困难。
在其它平台的gcc
gcc是一个强大,开源的编译器,因此被人移植多种CPU,多个操作系统下.
如移植Windows 下的mingW,Cygwin gcc.exe
几乎在每种CPU,如ARM,MIPS,PowerPC等都有相应版本,而且在很多时候是唯一的编译器,如龙芯.
gcc还有一个强大的功能,即交叉编译.即在A的操作系统下,编译B操作系统下的程序.
如嵌入式操作下x86的redhat 9.0下去编译ARM下的Arm-Linux 的程序
用于交叉开发的gcc
在arm下开发的gcc 通常叫arm-linux-gcc
将arm-linux-gcc解压后安装
将arm-linux-gcc其路径加入系统PATH中
将所有需要gcc 的地方改成arm-linux-gcc 重新编译,即可完成简单的移植
在嵌入式中,通常需要-static 静态编译.
编译后的可执行程序可用U盘或FTP上传到ARM开发板上
课内练习,将上一次课堂练习结果移植到开发板上
注意,首先修改开发HOST的对应用户的.bash_profile,假设是用户hxy,则在/home/hxy/.bash_profile 的PATH改成如下定义
PATH=/usr/local/arm/2.95.3/bin:$PATH
这样才可以直接用arm-linux-gcc
常见错误或警告
dl_list.h:42:7: warning: no newline at end of file
这个通常是由于源码里最后一行不是UNIX文本字符要求的空行,造成,这不影响最终编译结果,如果想去掉,直接在源码中加入一个空行即可
test_link.o(.text+0x308): In function `test2':
 : undefined reference to `dl_save_to_file‘
表示在最终链接时,没有找到dl_save_to_file这个函数的目标代码,原因有多种,如没有把这个源码包含进来,系统函数的笔误.找不到对应链接库
main.c:8:47: String.h: No such file or directory
某个头文件找不到,很大可能性是没有用-I参数把String.h所有在目录加入进来.
./main: error while loading shared libraries: libstr.so: cannot open shared object file: No such file or directory
应用程序找不到自己链接的动态库libstr.so,需要把libstr.so加入到环境变量LD_LIBRARY_PATH中
程序运行常见错误
1在busybox 下运行程序,
  /usr/bin/boa: /usr/bin/boa: 1: Syntax error: "(" unexpected
最大可以是这个程序是用x86 gcc编译的程序,然后在Arm-Linux 运行时,被不匹配的strip
课堂作业
请开发一个程序,使用结构定义
struct student{
  int no; /* 学号 */
  char name[64]; /* 名称 */
  float  height; /* 身高 */
};
要求创建3个学生定义,并给三个学生的三个成员都赋上值,
用一个printf 把一个学生的三个值打印出来,依次把所有学生信息显示
要求用vi编辑,并用gcc 在Linux下编译,测试通过
课堂练习
开发一个库函数,要求能对字符串的进行转大写转换.
要求把库函数,编译成静态和动态链接库各一个,取名libtu.a,libtu.so
编写一个测试程序,要求能测试libtu测试库函数
把上述过程写成一个Shell脚本,一次性执行

相关PPT

桑拿培训教程ppt:这是一个关于桑拿培训教程ppt,主要介绍了桑拿的起源与功效,及现代桑拿的发展与特点;服务员的礼仪规范;桑拿服务技能的培训;桑拿服务规范、服务用语及岗位职责;礼仪的工作内容等内容,欢迎点击下载!
马铃薯种植培训教程课件ppt:这是马铃薯种植培训教程课件ppt,包括了选用良种,选地整地,马铃薯的营养与需肥特点,施足基肥,适时播种,收获,耐氯弱的作物类型等内容,欢迎点击下载。
西门子PLC培训教程(PLCsim仿真)ppt课件:这是西门子PLC培训教程(PLCsim仿真)ppt课件下载,主要介绍了西门子PLCSIM V5.3的应用;PLCsim的菜单说明,欢迎点击下载。
《7_GNU GCC 开发教程ppt》是由用户封缄于2017-06-05上传,属于培训教程PPT。

标签:

相关PPT

缩略图

  • 7_GNU GCC 开发教程ppt
山东医学高等专科学校 | 西陆军事 | TPM管理咨询培训,5S,6S,现场管理,精益生产管理(JIT),设备六西格玛项目管理 | 南京翻译公司 | 汇师经纪(huishi365.com)是培训讲师的讲师经纪平台,专为培训机构提供优质讲师。 | 山东技校网 | 首页 | 木鸟民宿网 | 青岛猎头公司 | 小学生作文、初中作文、作文大全 | 在职研究生招生报名信息平台 | 南京翻译公司 | 小学生作文、初中作文、作文大全 | 在职研究生招生报名信息平台 | 沙巴体育 | IT培训班选达内,美国上市的IT培训机构 | 在职研究生招生报名信息平台 | IT培训班选达内,美国上市的IT培训机构 | 石家庄二中西校区 | 中国化工网 | 【新乡新东方】新乡英语学校 |