找回密码
 注册
关于网站域名变更的通知
查看: 76|回复: 0
打印 上一主题 下一主题

嵌入式Linux下最常用的C语言编译器GCC命令

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2023-2-21 18:01 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
GCC(GNU Compiler Collection)是Linux下最常用的C语言编译器,是GNU项目中符合ANSI C标准的编译系统,能够编译用C、C++和Object C等语言编写的程序。同时它可以通过不同的前端模块来支持各种语言,如Java、Fortran、Pascal、Modula-3和Ada等。
2 O9 X1 y# Q5 Z8 h5 c
穿插一个玩笑: GNU意思是GNU’s not Unix而非角马。然而GNU还是一个未拆分的连词,这其实是一个源于hacker的幽默:GNU是一个回文游戏,第一个字母G是凑数的,你当然可以叫他做ANU或者BNU。下面开始。
: I. Z+ T! P! X" [5 ?( E: f; z
- Q. E+ V* W$ Q( ?
一.CC编译程序过程分四个阶段

+ }3 A% `# X/ }9 |+ }. J8 ^& G( k; M8 g+ i& K
◆ 预处理(Pre-Processing)
+ ^" H& M" b5 z( c6 |& X◆ 编译(Compiling)
: Q- r% T; \1 k4 X: ^◆ 汇编(Assembling)-
& x; ?1 C2 K7 X, b◆ 链接(Linking)
; b. U& ^, Y  i, z* sLinux程序员可以根据自己的需要让GCC在编译的任何阶段结束转去检查或使用编译器在该阶段的输出信息,或者对最后生成的二进制文件进行控制,以便通过加入不同数量和种类的调试代码来为今后的调试做好准备。如同其他的编译器,GCC也提供了灵活而强大的代码优化功能,利用它可以生成执行效率更高的代码。

1 J% u0 O3 w8 a) ~; W/ C# i! j4 a" r; `& J! W5 q2 d7 F
GCC提供了30多条警告信息和三个警告级别,使用它们有助于增强程序的稳定性和可移植性。此外,GCC还对标准的C和C++语言进行了大量的扩展,提高程序的执行效率,有助于编译器进行代码优化,能够减轻编程的工作量。
9 o& {! q- f7 d: G6 W( b7 B0 S
1 Q3 F( Q  ~' M* S* N# u+ N
二.简单编译命令
" q6 ?8 z; v. V, J0 ^5 P
9 {* X3 d4 y, w  ^
我们以Hello world程序来开始我们的学习。代码如下:
3 ^2 m- T/ l6 e/ f! M2 T8 i

& C! @  l0 x8 E/ [4 F9 u
/* hello.c */2
4 d. w6 e% C5 F# ~! G6 e" m
7 p' u3 @7 L& f& a
#include

! a6 _5 {  U0 ]- D6 ]4 D
2 d" q8 w. f# K6 q6 h6 O6 C
int main(void)

' |1 \9 w& R  I0 A7 {
1 {* p( s# t! E" J0 Y- F
{,
  N; r( o/ q+ U. W
/ F$ r& U" W3 y+ o! x/ J
printf ("Hello world!\n");*
5 I4 k+ d0 ]  h$ y" e) L
0 t2 I5 a2 X. O* ]# U' q# c' d
return 0;

0 T+ H6 m- b4 Y3 f1 K6 ^: C4 z* n  W: L, B9 @7 t
}

- M/ y9 [" u% W) A# s+ J+ y) U; L3 m2 i+ k9 E7 c
1. 执行如下命令:$ gcc -o hello hello.c

9 f0 ?# m. E& F" m- `% }! c$ C8 S" E
运行如下 : $ ./hello

9 l3 O+ Y! C$ K! _1 h/ o1 I0 G* T
. x0 S% L, T( W& R' O# e, ?2 N
输出: Hello,world!
0 Z/ M' ^8 M$ p' a- `0 A% U8 H& c4 U
+ A: L) S6 e9 k1 W! k1 a% I6 R
2. 我们也可以分步编译如下:
5 q+ ^0 O* C( @' \( q" R6 n7 E, g( F' g
( Y4 P1 R! p" Z- s: l
(1) $ gcc –E hello.c -o hello.i5 K3 n4 {7 d) Y, [% D  Y
. y( v+ \+ }- y; v" |+ ]" G# e$ ^! R3 _' J) ]% K0 o3 v' u% O. t$ H8 T: o3 O. ]; B
% N  K9 `$ u6 ?# J' t3 K& C
//预处理结束& c4 J& |7 @- k9 Y, ]: j' z4 w
8 t' ^; t1 R1 b; |3 X
; m* w: m( m( _2 S) T# U
//这时候你看一下hello.i ,可以看到插进去了很多东西。: f! Y# U7 }3 t" T8 ~3 w0 M
" r! Z/ U( F  O7 L, r4 Y% V+ `
( ]' p# J7 \' p- k2 e6 u$ S8 |. g$ Q
(2) $ gcc –S hello.i1 E- L4 p7 i8 _" l! ~! u# D
- {9 e( n( R  [5 d$ w! Q/ }, L% P& l  f
/ h1 ?; q* T8 n3 i
. o0 [, }# c( p$ a//生成汇编代码后结束# D- S2 C2 `7 z1 ^
( S2 C' z3 g- n6 c6 Z
: ?8 m9 u9 c& n% P7 {4 A4 q$ `2 q& s4 Z6 `2 B2 y- d9 `2 u: `9 w
(3) $ gcc –c hello.c
' G% Z9 E. T, e5 x) d7 N5 B1 z# r5 _5 q
* N" B/ g" j1 l+ u  o! Y# t' F5 ]
, {2 e+ m) }: y, |. ~( O6 L或者:( w3 L& W8 v4 k; `" q
  j! `" q  u0 X3 r4 E

' U; K- S5 }& M. F3 B$ gcc -c hello.c –o hello.o2 F; T/ D+ u; Y0 A- L6 T# s0 n, C
8 o! r/ P, h: b3 F
2 i- J3 v" J& U  J. l2 L/ b! o# E) l3 }" g; H6 S$ M8 k) K
或者:: o' [2 S& m; _$ U7 D# F# r* y2 F9 n

, f+ x( @2 k% m! M9 C9 d# _1 e8 l. L6 _* M' C; F  K/ g
2 ^3 j" ?' N( e" S+ p$ gcc -c hello.i -o hello.o  w* F; W0 L& ]' J& R  E5 C* O( K: ]) x; I
) |2 C9 @& U- V5 H2 S# t) C9 r- J4 O7 A* C
: M: c# ^' Q8 x, x' K+ p
8 }, [' a# ?1 D//编译结束5 {; s9 O. ]$ i; Q4 o& L* n# p4 \1 z: V: O  _/ Y
+ \" e" Q6 N; ^8 U4 V. y% I$ q
8 H8 T. @- q0 [) H) X( F9 ~
4 V3 d; y3 I' y//生成 hello.o文件
8 c4 T6 V2 }6 d. g  W+ J# f/ j5 q/ ~% u# _7 b* L# R4 {$ d5 \
  N" L" Y! @- `5 D
. P5 L* b& z% y% c" k! ~(4) $ gcc hello.o –o hello.o0 h/ G/ M) D( A1 {; ?: ~& v$ ?
7 u5 ?# p/ y* @* X$ w4 C3 t& q9 T/ h& ]
  {$ Q7 E  }8 g3 B( ?# |: k. t8 o0 A0 P
或者:9 x. R; V  o! d4 {7 _
) q- B% I& i% G6 `/ p5 Y: n& A  P$ c3 d; x& D2 _1 E" C  T( |2 _+ |; \* I
$ m! L7 k* ~9 R9 ^% ]% h3 W( v4 W, S: `' K) p' \7 w) O' Z5 C. n
$ gcc –o hello hello.c3 V1 P$ E8 x1 f
% t) U1 L! S9 x) B4 I% U7 |* o  g- B, d7 S. v, O) V% r" a- F
4 {) x3 T; A# _* @+ r! D8 F: ]1 w8 t4 j6 F8 E8 j
//链接完毕,生成可执行代码, }& p/ s; O8 S, C1 g- z% T7 U* e: V' A9 |8 y# i* ~  @

! W; h4 x( h. ^3 b' c1 z" Y! k( q3 S7 l6 @, D' Z
! S, Y' \( K4 M8 `8 y# c3. 我们可以把几个文件一同编译生成同一个可执行文件。0 |: z0 l3 I  K6 [
+ |6 |4 B: l) v  K, s) d
( N& t% B* v" z1 H5 t% ]- s# T/ o
( y1 A; J7 b6 a7 n) |比如:一个工程有main.c foo.c def.c生成foo的可执行文件。% Z; J& ?( ?$ b2 ^- p
/ M. H4 u5 W8 l) r1 @4 Q0 _$ f9 U* Q) {7 @
4 T6 P6 \, j4 r& w, I
编译命令如下:
4 X1 @8 {# ?( v! R7 S4 ]$ u$ q. O+ E: [4 s5 d; n4 c/ e4 P" B, s

6 T# a5 {+ w  o/ b4 F* j( n$ gcc –c main.c foo.c def.c –o foo% C  x9 h' c% Z3 ]/ _/ }( v
2 R+ i% n8 [$ O/ M+ _% }' {6 p- Y, h! R" ]/ n6 l0 [/ |0 p* Y+ {
$ J/ a- f  P! k; w5 R3 z. F8 i8 _8 e2 S
# f6 f4 o$ m/ s8 B% }或者:2 d* b8 `" @; G9 g' a% c9 \+ n) m: K3 E! X2 r: a

; ^1 A" h8 c3 ^% u  h4 g* g: I0 F0 s" @6 [( k" b& {& N
* T( J* s& a* W$ gcc –o foo main.c foo.c def.c3 Z- e, o  `: N+ F! [' |
) i; w9 @/ V0 |8 |1 u8 W. F
7 a; @* |# C& R3 t9 A$ f, R% i: Q7 ^6 R* ?" N( K# F# ]  _- }& R
: g: f+ C% \; H( I三.库依赖% U6 I# v% q- b" F! N) {$ o) k9 E1 G

  `. }' h! H( z& |! o) y" c$ A* s( g; U, n" v: Y$ @: u6 W
+ S, }' u. H& r) f$ d" b8 T函数库是一些头文件(.h)和库文件(.so或者.a)的集合。Linux下的大多数函数都默认将头文件放到/usr/include/目录下,而库文件则放到/usr/lib/目录下,但并非绝对如此。因此GCC设有添加头文件和库文件的编译选项开关。* x* v5 {$ R0 V3 z# f3 r* p" E7 S- y  j  P# g; T  b9 d
: s8 b$ r. i7 E+ P
3 Z3 }! p) q) `6 [+ ?: w; D/ a/ O# Y+ X8 Y! E
1. 添加头文件:-I, y$ [1 }( z/ R  E; @! T
# C  O& M. `% m% v/ d( ^5 p9 x0 f5 I4 X- w5 O+ K9 Y1 _8 e
( m, _. ?& m, O  n- F5 v' k& S! Z* ~7 M% T3 U' Q+ B6 C) X
例如在/home/work/include/目录下有编译foo.c所需头文件def.h,为了让GCC能找到它们,就需要使用-I选项:
, Q6 V) t, r5 J6 U3 w1 L7 [6 q' Y. H
. @  r6 `6 a" ^. \& j1 w/ m8 D5 K! g8 D( J. v* n9 W7 c+ o) b  M/ q4 G
$ gcc foo.c -I /home/work/include/def.h -o foo
& _# D6 D8 h" _  N& X( h# Z* }7 K) [8 n3 |- k; [7 J5 M! [* N9 @! a
# @- f# V0 v+ V- }5 j! g& V
4 U  s: P1 [- R% [$ O* J  f- ^5 Z2. 添加库文件:-L$ x  I  o0 g5 v) T5 f  K( O; J& C1 |* e" k- K

8 Q8 Z2 E( N% _9 i# k& w, t3 \6 R* d) k  V( z8 Q/ Q) L$ R1 M
例如在/home/work/lib/目录下有链接所需库文件libdef.so,为了让GCC能找到它们,就需要使用-L选项:% b* p3 Z" v0 k8 t4 b8 [. ^$ ^
! Y2 L/ H5 y0 ?) o5 }0 ]+ Y( p% `: z6 z& s& [  N) N4 l

& r8 ?! S5 O/ j7 R* C) Y8 Y$ gcc foo.c –L /home/work/lib –ldef.a –o foo- x+ U, n" T' c4 c3 o# A5 B
7 y( A1 F- ]* L7 {

' T( n4 E2 N! G1 x4 Y. K$ [' V说明:-l选项指示GCC去连接库文件libdef.so。Linux下的库文件命名有一个约定,即库文件以lib三个字母开头,因为所有的库文件都遵循这个约定,故在用-l选项指定链接的库文件名时可以省去lib三个字母。, {! A0 \% b3 R! u# A5 I
6 E' K0 V4 r' p* m% C( ~2 w9 E  D' j9 J7 n7 r5 @# p: u, K
1 o  ?! R, T* j* R
* m0 c" ~/ f9 f8 ^, E3 o题外语:' b; u( ~# x2 t+ m1 g3 K3 h% h. m
5 [5 Z' L0 }1 f1 ^3 X7 y$ N% i/ R8 I
' J* b4 R8 m) p! S
5 O$ L4 L, f) X8 U4 hLinux下的库文件分为动态链接库(.so文件)和静态链接库(.a文件)。GCC默认为动态库优先,若想在动态库和静态库同时存在的时候链接静态库需要指明为-static选项。比如上例中如还有一个libdef.a而你想链接libdef.a时候命令如下:# {: p+ J2 v9 u* Y9 ]5 a/ j( `+ @7 q. `* ?
; e9 |1 ?, i# U! ?& P$ f% N
! y& T5 J. y0 Z0 L3 B2 e6 X9 s7 |/ M) c+ d
$ gcc foo.c –L /home/work/lib –static –ldef.a –o foo8 Q- a7 L: p4 S: \4 u- g

; G* D0 Q6 x% ?) f% I# E( |7 h3 X' v1 M# a8 e7 U$ V: h$ t3 c0 P
四.代码优化6 P: q3 |4 }# U- K7 b8 V2 n: p0 G5 [: Q" S& ~: Q4 f9 A
4 b: G3 y/ P, a" l# B7 C2 y
1 r3 [) D. K( k- T# }7 M+ u1 O" ]3 V; H( }! d4 c* u8 y1 j! u# L  N3 o; S& J3 J
GCC提供不同程度的代码优化功能。开关选项是:-On,n取值为0到3。默认为1。-O0表示没有优化,而-O3是最高优化。优化级别越高代码运行越快,但并不是所有代码都能够加载最高优化,而应该视具体情况而定。但一般都使用-O2选项,因为它在优化长度、编译时间和代码大小之间,取得了一个比较理想的平衡点。* X7 O/ n& Q; z6 t1 U2 {) [  P! `+ Y1 P9 x
- T( ?5 G) @! M7 D+ p4 J5 R: a# c$ ?; x6 K
" J, y; T. S- j7 i! `7 q
0 u  `  d) T2 A* i3 y以下这段说的比较详细:* q3 p% P% M& j, \8 ?/ l
9 A" H5 a6 ~0 c; p' q5 G  I  t4 r8 U
& N0 u/ I$ g) p! m# H7 T" _! x, d' e
2 N0 i. [8 Z+ k, o! W0 i编译时使用选项-O可以告诉GCC同时减小代码的长度和执行时间,其效果等价于-O1。在这一级别上能够进行的优化类型虽然取决于目标处理器,但一般都会包括线程跳转(Thread Jump)和延迟退栈(Deferred Stack Pops)两种优化。选项-O2告诉GCC除了完成所有-O1级别的优化之外,同时还要进行一些额外的调整工作,如处理器指令调度等。选项-O3则除了完成所有-O2级别的优化之外,还包括循环展开和其它一些与处理器特性相关的优化工作。通常来说,数字越大优化的等级越高,同时也就意味着程序的运行速度越快。
1 L1 L$ w) ~4 Q  @% j1 I8 U% N$ k% Q
1 B! z, k- A, ?+ \* O6 r3 n* \* E
下面通过具体实例来感受一下GCC的代码优化功能,所用程序如清单3所示。' t7 Z) M1 \' l! U0 J2 z: k6 ~; j
8 f  r) W* M8 Y, g! |  N9 U6 v
# T+ h% \- G. I0 g; I5 u/ Y8 I1 F# M6 u% H0 a% X" v- P
/* optimize.c *// C5 R) y4 a) [- T) i, T( V% j; X8 i; N7 i
#include <stdio.h>$ x% z( i2 B% }$ ^
( i& p* L. P' {0 d4 ~: N# z
1 J. O+ b5 u+ B4 s- }int main(void)& r- i$ O: w% V, k1 b
{# M1 M- z& v, A2 a2 |' ~2 B" O
double counter;# i  v+ T5 e, n5 _. L2 ?* }9 j
  P6 A( q& \7 {! ]& D! Ddouble result;) E" H5 `  u  [- P* g: u5 \: d' J
double temp;
- i% k$ m+ X. e% m" \: G2 d. \, M$ T$ wfor (counter = 0;& k1 S6 v5 {2 {1 J# z
counter < 2000.0 * 2000.0 * 2000.0 / 20.0 + 2020;& B* t7 \  J& ?
counter += (5 - 1) / 4) {' Q. r. h" l# L" e3 }
temp = counter / 1979;& [/ T3 I) N' h0 r0 ~( K9 N/ s: c( K  U6 b# ~/ a5 A: j& n0 D
result = counter; " {( @/ \6 j. S. Q3 E+ y0 @0 @# u: K6 f
}! o) p7 \* J) ?1 T; s+ y$ z" H( D+ t# B$ ^
printf("Result is %lf\n", result);" T$ l& j6 @. E) g: o
. L! _) i& a. \% a( lreturn 0;# f2 `9 n0 e7 ~& h/ A
& f7 u' E- X2 C4 t* M1 b}
6 ]5 c% l6 U, X1 K1 ^1 `+ {) E) _0 c9 d- w3 h4 J4 X$ ?" J* E
  T$ c- I! r8 d$ m1 l
, G1 e/ C3 v) C9 s$ v8 K# S; _- v% N! a7 q/ U/ V( u2 e4 o2 W7 P/ n
首先不加任何优化选项进行编译:2 Q8 Z7 I$ e) T7 m. R; M1 f3 [# F/ T  U) B
) }- Y1 a6 {/ r5 Q: i! H
2 @4 Y$ l$ T. q" U0 U" S* W
# gcc -Wall optimize.c -o optimize
. W! x6 \8 a( D0 j- R# S4 Z' E  Z) T% U# u. O6 z) t$ z
+ a' i8 S! ?3 Z0 I/ N0 Y. b+ ^  u3 T6 `+ `. y* ?7 q+ U. L
借助Linux提供的time命令,可以大致统计出该程序在运行时所需要的时间:( [) ?: G2 o6 ~
2 Z5 R( t5 \5 P7 i  i, f4 C7 W% i; V8 j1 T. z' S1 X: d8 T( n4 S' e# t1 b0 n
; ~9 x' v' z. p( R2 g; m- ~' R. T; t! `& B
# time ./optimize* z. X5 q3 g; u) Y, C2 _9 t% D6 S$ J$ ~
Result is 400002019.0000001 a; f0 q6 T; L* B7 b. W
real 0m14.942s  m' [9 I  L7 O' X$ H. g3 C* p1 J. f. q6 C% {+ P' `- i
user 0m14.940s; q- }/ z0 j! E
/ X, a- O# z8 l* H0 O- gsys 0m0.000s
( C' C7 ~( f. a( G  M+ N+ [8 T' k( `* M  j! o
7 a; N9 O$ `3 A1 c
5 }) ~8 ~4 V( X/ g# k接下去使用优化选项来对代码进行优化处理:
2 Q4 c5 t0 y, R* J. p
+ \3 s8 E# b. B2 |% x3 }& s7 V$ r! v* `9 u' X5 `" d7 {5 _
# gcc -Wall -O optimize.c -o optimize2 R2 p9 V: j- D4 ]: y1 I
  z( p% g7 P7 k+ ]0 {9 k0 K
& L6 k3 N4 b" f2 `& i1 Z  C( G5 l2 K' S& O3 ~3 q; S8 B. {" o
在同样的条件下再次测试一下运行时间:2 B, K* ?% K( h3 z
8 b$ {2 y9 Y5 d# x7 N) h
, @( B4 g# B; [8 Y/ g2 q
# time ./optimize3 k) a) S3 v8 ~. u4 q) n) l
( [& g6 T/ I! X  W1 JResult is 400002019.0000006 H; ^5 p) H. Z; B, z/ B& w8 f) M" W0 t) |/ H) I
real 0m3.256s. b  N5 |7 f! R# z8 J- V
, R) w2 G; T, I) S4 r/ \4 ~. Y5 @2 Ruser 0m3.240s2 d$ r0 o4 {; s  R5 L2 w/ F* B9 T
- A' h/ b- b  ~$ w$ o& Usys 0m0.000s
5 R- ^4 W4 ]! K" P% d  m2 }; {3 O& e" E
* e7 \! n; S7 M1 i4 A: _5 K7 w& G/ F
对比两次执行的输出结果不难看出,程序的性能的确得到了很大幅度的改善,由原来的14秒缩短到了3秒。这个例子是专门针对GCC的优化功能而设计的,因此优化前后程序的执行速度发生了很大的改变。尽管GCC的代码优化功能非常强大,但作为一名优秀的Linux程序员,首先还是要力求能够手工编写出高质量的代码。如果编写的代码简短,并且逻辑性强,编译器就不会做更多的工作,甚至根本用不着优化。; T' ~3 b7 b4 a% L: e+ n" X% O2 `* x
  N8 d' _: v3 q5 E/ b; [- J; G
优化虽然能够给程序带来更好的执行性能,但在如下一些场合中应该避免优化代码:
  H! k5 T5 J: Z/ o/ B* t0 U7 e3 [/ n  z$ z1 i$ i4 g! e* B/ K2 j4 D/ t
7 _5 J9 g' n' _+ M  f5 B
1 |1 l# V9 y! m8 g7 n◆ 程序开发的时候优化等级越高,消耗在编译上的时间就越长,因此在开发的时候最好不要使用优化选项,只有到软件发行或开发结束的时候,才考虑对最终生成的代码进行优化。. E- M  b% E$ m4 F* _; h  t
9 c- r2 o; a8 I# O4 O+ Z; Q' F' N' L# _: w
◆ 资源受限的时候一些优化选项会增加可执行代码的体积,如果程序在运行时能够申请到的内存资源非常紧张(如一些实时嵌入式设备),那就不要对代码进行优化,因为由这带来的负面影响可能会产生非常严重的后果。7 j2 s3 z0 [2 A; z5 K/ Q/ `
2 Y; s4 T/ I) o* n' C" J& }$ Y3 C  K2 Z, S% X- n0 A
◆ 跟踪调试的时候在对代码进行优化的时候,某些代码可能会被删除或改写,或者为了取得更佳的性能而进行重组,从而使跟踪和调试变得异常困难。
: @8 W: S% y4 _  Z) V
! w6 U7 w; j; k9 U2 k
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

EDA365公众号

关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

GMT+8, 2025-7-29 23:42 , Processed in 0.125000 second(s), 24 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

快速回复 返回顶部 返回列表