你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

嵌入式Linux下CC编译程序过程

[复制链接]
gaosmile 发布时间:2020-3-30 12:47
GCC(GNU Compiler Collection)是Linux下最常用的C语言编译器,是GNU项目中符合ANSI C标准的编译系统,能够编译用C、C++和Object C等语言编写的程序。同时它可以通过不同的前端模块来支持各种语言,如Java、Fortran、Pascal、Modula-3和Ada等。
: P& B- h  H6 k( C- I
穿插一个玩笑: GNU意思是GNU’s not Unix而非角马。然而GNU还是一个未拆分的连词,这其实是一个源于hacker的幽默:GNU是一个回文游戏,第一个字母G是凑数的,你当然可以叫他做ANU或者BNU。下面开始。
+ {0 m+ M' }# l  t4 Y
一.CC编译程序过程分四个阶段

; _0 U, E* E$ g0 f/ Y# t4 Z
◆ 预处理(Pre-Processing)
: b; y* g/ w) t8 [+ }; S
◆ 编译(Compiling)

; w: D& {' K& T3 u, P  U9 f
◆ 汇编(Assembling)

+ e' {2 @  h4 h& G% d- H
◆ 链接(Linking)
* F. P  C' n# s* A6 b
Linux程序员可以根据自己的需要让GCC在编译的任何阶段结束转去检查或使用编译器在该阶段的输出信息,或者对最后生成的二进制文件进行控制,以便通过加入不同数量和种类的调试代码来为今后的调试做好准备。如同其他的编译器,GCC也提供了灵活而强大的代码优化功能,利用它可以生成执行效率更高的代码。

' f, _$ w& T3 m9 _  L
GCC提供了30多条警告信息和三个警告级别,使用它们有助于增强程序的稳定性和可移植性。此外,GCC还对标准的C和C++语言进行了大量的扩展,提高程序的执行效率,有助于编译器进行代码优化,能够减轻编程的工作量。
. P( b2 o3 t5 s/ }/ `( U
二.简单编译命令

% m4 z5 \  ?1 [
我们以Hello world程序来开始我们的学习。代码如下:

' W% k+ T' @0 |0 H9 X; Z) r
/* hello.c */

& B4 l8 U, b& ^7 O- P4 L
#include
1 S+ [* o9 q, f& ]
int main(void)
4 C6 `) L, m: t
{
+ H. L( q8 ~+ H
printf ("Hello world!\n");
" S/ d- N  ~+ G( f
return 0;
8 E- M$ I- d" C$ t  l: W, E: e- r
}

' o( `2 n$ g4 d3 R: q
1. 执行如下命令:$ gcc -o hello hello.c

) t' ?" O8 H; b, N, ?/ F
运行如下 : $ ./hello

) N& D/ g: d1 \! s
输出: Hello,world!
) a( Z* Y: G: H% y3 X# q4 ~
2. 我们也可以分步编译如下:

" ]% _- C0 k6 P' }+ @
(1) $ gcc –E hello.c -o hello.i
, ~: I6 L/ h  ?8 x* A- @! u
//预处理结束
( C8 a% J8 L: O: c4 j$ B6 U
//这时候你看一下hello.i ,可以看到插进去了很多东西。
: v  Y3 C5 c+ Q4 x- M0 T5 _
(2) $ gcc –S hello.i
+ j8 v7 {, f" t/ K5 y
//生成汇编代码后结束

: v, W- C5 U* l) |# m9 Q$ j
(3) $ gcc –c hello.c
" C* p! g0 v# o' f* \( F
或者:
) r' ]7 G6 Q9 s" h+ z5 s
$ gcc -c hello.c –o hello.o
  g% R; Y0 n4 R" T4 H) s! j
或者:
( w4 n1 ?; W1 N) U1 _
$ gcc -c hello.i -o hello.o

- \) X) O( n( R5 P5 H/ g5 i
//编译结束

% z1 h# u8 q- w/ m7 i# g
//生成 hello.o文件

; T. c" p, S  G
(4) $ gcc hello.o –o hello.o
- n) V, M5 t, V+ w0 N0 d
或者:
- ^- v7 C8 T* b
$ gcc –o hello hello.c
: M6 a+ y; q% @
//链接完毕,生成可执行代码

8 g) p) }; N* g+ J
3. 我们可以把几个文件一同编译生成同一个可执行文件。
: e4 m7 d' i4 g- |4 O$ t$ `
比如:一个工程有main.c foo.c def.c生成foo的可执行文件。
) S1 g" o0 K0 {$ |; w1 R
编译命令如下:
0 W: D5 }$ `9 I( V' x4 o: O
$ gcc –c main.c foo.c def.c –o foo
+ B9 m7 {0 r! |4 v* t3 K7 I+ T
或者:
- D. p+ N/ T& T# r3 s
$ gcc –o foo main.c foo.c def.c

8 U7 s  T6 L3 b$ J
三.库依赖

6 }" |6 b" A5 P
函数库是一些头文件(.h)和库文件(.so或者.a)的集合。Linux下的大多数函数都默认将头文件放到/usr/include/目录下,而库文件则放到/usr/lib/目录下,但并非绝对如此。因此GCC设有添加头文件和库文件的编译选项开关。

+ s+ K; g# _- C- y
1. 添加头文件:-I
- m# k1 Y# m7 P- p3 a
例如在/home/work/include/目录下有编译foo.c所需头文件def.h,为了让GCC能找到它们,就需要使用-I选项:
# y: \, G( \9 I
$ gcc foo.c -I /home/work/include/def.h -o foo

* n4 c) D# q. u0 L( T$ d
2. 添加库文件:-L

  ]  U# f8 h6 g: ^; \
例如在/home/work/lib/目录下有链接所需库文件libdef.so,为了让GCC能找到它们,就需要使用-L选项:
. z+ x1 i. L4 S) t! V* J
$ gcc foo.c –L /home/work/lib –ldef.a –o foo
/ ~4 l% v3 o4 C& H$ ^& i. M% z
说明:-l选项指示GCC去连接库文件libdef.so。Linux下的库文件命名有一个约定,即库文件以lib三个字母开头,因为所有的库文件都遵循这个约定,故在用-l选项指定链接的库文件名时可以省去lib三个字母。
" S! A, g& N+ U, ?' g7 W( l( J
题外语:

7 l3 y+ ~1 L9 U% ~$ @: I
Linux下的库文件分为动态链接库(.so文件)和静态链接库(.a文件)。GCC默认为动态库优先,若想在动态库和静态库同时存在的时候链接静态库需要指明为-static选项。比如上例中如还有一个libdef.a而你想链接libdef.a时候命令如下:
/ a6 P- E) N, ~* I
$ gcc foo.c –L /home/work/lib –static –ldef.a –o foo
+ O; F- {7 m: O* ^
四.代码优化

: M: O0 |& O, i0 L' _: J, n) q
GCC提供不同程度的代码优化功能。开关选项是:-On,n取值为0到3。默认为1。-O0表示没有优化,而-O3是最高优化。优化级别越高代码运行越快,但并不是所有代码都能够加载最高优化,而应该视具体情况而定。但一般都使用-O2选项,因为它在优化长度、编译时间和代码大小之间,取得了一个比较理想的平衡点。
! [8 F# |4 A- I6 o0 }# d6 h
以下这段说的比较详细:
9 v) M( \+ K* g. R- d
编译时使用选项-O可以告诉GCC同时减小代码的长度和执行时间,其效果等价于-O1。在这一级别上能够进行的优化类型虽然取决于目标处理器,但一般都会包括线程跳转(Thread Jump)和延迟退栈(Deferred Stack Pops)两种优化。选项-O2告诉GCC除了完成所有-O1级别的优化之外,同时还要进行一些额外的调整工作,如处理器指令调度等。选项-O3则除了完成所有-O2级别的优化之外,还包括循环展开和其它一些与处理器特性相关的优化工作。通常来说,数字越大优化的等级越高,同时也就意味着程序的运行速度越快。
" `0 @) {: Y, u8 B
下面通过具体实例来感受一下GCC的代码优化功能,所用程序如清单3所示。
4 f" G; i6 j3 a0 {9 T' }% V$ w
/* optimize.c */
#include <stdio.h>
" U8 p/ r( Q8 _9 F1 m9 u
int main(void)/ Z6 O! M, A5 ?7 L* M9 Q3 U
{
double counter;
double result;
$ C; x; p( l. f5 K" A6 v7 Q9 r6 `double temp;
" P# c3 m# ^9 |  L! m" O1 Gfor (counter = 0;
) t; [5 k& }, P+ G8 e) Y8 z5 Q2 Icounter < 2000.0 * 2000.0 * 2000.0 / 20.0 + 2020;
5 w& K& k$ |3 `& Q; }! N( u! Wcounter += (5 - 1) / 4) {
- o& |# U; E1 j9 [3 I! p- Y* Jtemp = counter / 1979;
+ }; K$ z) y" Y2 P: G( E$ }$ aresult = counter;
! T& Y1 l8 V8 o1 O/ Y4 a% J6 y}. }& u! m3 l; k* B& b
printf("Result is %lf\n", result);# b) i4 w7 b- @
return 0;8 i, v% C3 }& N1 J: A9 U
}
. ^0 Z; d8 m  ]
$ Y7 Y' v- d3 q/ ?  W. _
, l( A  A5 D% a) }
首先不加任何优化选项进行编译:
6 I) M6 C1 d, u

9 u" s' V' x5 K
# gcc -Wall optimize.c -o optimize
6 _& [! e/ T7 j: ^8 }3 _6 f# n) Y7 R
4 C0 f8 V+ A3 q1 B7 g8 r2 Q' o
借助Linux提供的time命令,可以大致统计出该程序在运行时所需要的时间:

9 T$ D& m/ [" v! L$ N

4 N$ P0 g' \, I9 R) ~% Q- k
# time ./optimize
8 [5 T$ h+ j/ g: o6 L( y0 ?Result is 400002019.000000
3 y& ^3 L. H! k) U' areal 0m14.942s' L4 o8 H  k9 k
user 0m14.940s
/ d: x2 j- K1 r7 c$ q7 Tsys 0m0.000s

# v; ?4 n0 P$ L7 ]$ J% c( X) C! ^

% q/ {9 `0 X, q3 q( r接下去使用优化选项来对代码进行优化处理:
* L) l/ f9 P8 z) N$ \- T
, i1 ]  T6 J$ R9 S) [# X$ Q
# gcc -Wall -O optimize.c -o optimize
# ?6 j, M  U: K! \
3 j) {7 R: P2 E% a  T5 X4 M8 [
在同样的条件下再次测试一下运行时间:
, Q& i7 s6 U" c: c9 ?+ C
/ H; l# n  `+ j- P+ B5 \1 `
# time ./optimize" A; [: M9 x; Q3 d
Result is 400002019.000000
, P9 F/ X# x/ e2 @' Sreal 0m3.256s
4 c* w, @  k2 ^( h$ }% ?8 vuser 0m3.240s! {3 i6 T- l0 K3 B
sys 0m0.000s

4 ?8 f. g! ?3 J/ K- A
! P6 {0 t7 s5 T. d: l" D
对比两次执行的输出结果不难看出,程序的性能的确得到了很大幅度的改善,由原来的14秒缩短到了3秒。这个例子是专门针对GCC的优化功能而设计的,因此优化前后程序的执行速度发生了很大的改变。尽管GCC的代码优化功能非常强大,但作为一名优秀的Linux程序员,首先还是要力求能够手工编写出高质量的代码。如果编写的代码简短,并且逻辑性强,编译器就不会做更多的工作,甚至根本用不着优化。
3 n$ e1 f( E  i, u; L0 G, a4 A
优化虽然能够给程序带来更好的执行性能,但在如下一些场合中应该避免优化代码:
! {4 |; F8 B) @* Y

: W& ]+ G( Z, Y( a3 C
◆ 程序开发的时候优化等级越高,消耗在编译上的时间就越长,因此在开发的时候最好不要使用优化选项,只有到软件发行或开发结束的时候,才考虑对最终生成的代码进行优化。
& x$ J8 z5 B! y9 C4 D6 z5 ^4 `! q
◆ 资源受限的时候一些优化选项会增加可执行代码的体积,如果程序在运行时能够申请到的内存资源非常紧张(如一些实时嵌入式设备),那就不要对代码进行优化,因为由这带来的负面影响可能会产生非常严重的后果。

1 B5 k6 S8 d0 ~! H5 C1 _◆ 跟踪调试的时候在对代码进行优化的时候,某些代码可能会被删除或改写,或者为了取得更佳的性能而进行重组,从而使跟踪和调试变得异常困难。
赞 收藏 评论0 发布时间:2020-3-30 12:47

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版