找回密码
 注册
搜索
查看: 5147|回复: 0

C语言高效编程的几招

[复制链接]
发表于 2006-11-14 15:25:09 | 显示全部楼层 |阅读模式
引言:! s9 f/ w9 u! X9 z  i
  编写高效简洁的C语言代码,是许多软件工程师追求的目标。本文就工作中的一些体会和经验做相关的阐述,不对的地方请各位指教。  I  f, k# k: n$ }" J, u
4 @6 l5 ^6 M; n
第1招:以空间换时间: _0 k! {3 j5 \$ h+ j
) X: w- Y- k, j+ B! k& {  u
  计算机程序中最大的矛盾是空间和时间的矛盾,那么,从这个角度出发逆向思维来考虑程序的效率问题,我们就有了解决问题的第1招——以空间换时间。7 N0 H& e3 a2 b" I' m* S6 w
例如:字符串的赋值。2 {' W- z( Z! j4 M
方法A,通常的办法:
/ B- V9 m+ S# w2 K+ p- @$ p#define LEN 326 }3 C. r8 ~& f! A( v* h* _! }
char string1 [LEN];
0 ~6 B6 p" C$ V; {  j/ x2 l' Lmemset (string1,0,LEN);% Q: f* a' X: A0 z7 S% _& J; F( M$ H
strcpy (string1,“This is a example!!”);+ Z0 N. H7 v" I/ H1 x
方法B:
) D$ e  R/ p- x3 w7 [) z7 T9 |3 qconst char string2[LEN] =“This is a example!”;
3 L" B" G& V5 O0 N$ zchar * cp;: K- ~1 a4 e+ l0 u# @
cp = string2 ;- l; j8 t; e" T6 f) X) L
(使用的时候可以直接用指针来操作。)+ R6 v) J. j8 R. @% \
/ p- k: v& v* t
  从上面的例子可以看出,A和B的效率是不能比的。在同样的存储空间下,B直接使用指针就可以操作了,而A需要调用两个字符函数才能完成。B的缺点在于灵活性没有A好。在需要频繁更改一个字符串内容的时候,A具有更好的灵活性;如果采用方法B,则需要预存许多字符串,虽然占用了大量的内存,但是获得了程序执行的高效率。
& ]0 B8 R$ A7 T5 n  J
* j7 X! o' r, X2 _. c( o  如果系统的实时性要求很高,内存还有一些,那我推荐你使用该招数。
1 A3 A3 I! k+ e" \% y0 H4 D
7 ^/ r  p/ v. A( d$ J  d) r/ v2 ^  该招数的变招——使用宏函数而不是函数。举例如下:' N) u: K; A2 k
方法C:
3 o- h# c" ?( b4 }  u+ w#define bwMCDR2_ADDRESS 4/ i) Y+ L; j2 _& O0 y. `
#define bsMCDR2_ADDRESS 170 d+ [5 a  s7 Y
int BIT_MASK(int __bf)1 u, ?2 J5 e* T/ \1 B& Y7 ]  [
{' ]4 h4 L0 a8 ^/ A$ _9 W' q( `# C
return ((1U << (bw ## __bf)) - 1) << (bs ## __bf);
6 m0 w( ~' n6 S2 M( d( r}8 X$ }$ W8 Y* N' t! p
void SET_BITS(int __dst, int __bf, int __val)1 P$ j. o" X( j: _3 W
{
- E' E: ?9 r4 q( Z2 Z1 v* `0 x__dst = ((__dst) & ~(BIT_MASK(__bf))) | \
7 @: S( s4 P) |  a7 Q5 J(((__val) << (bs ## __bf)) & (BIT_MASK(__bf))))9 V7 h" H" h  k3 }+ P9 a( {$ n
}
8 a0 C: O: t- `  X  ^( T
* D9 N: E* @% d( q" JSET_BITS(MCDR2, MCDR2_ADDRESS, RegisterNumber);, X2 l; P1 t: Q- Q2 h, M* T7 Y* x
方法D:" U9 Y0 I1 `+ t4 X& s# v) k
#define bwMCDR2_ADDRESS 4+ T* l; p. h% ~3 w8 N2 }/ k* T1 ]
#define bsMCDR2_ADDRESS 17
% Z; l; X, j+ `#define bmMCDR2_ADDRESS BIT_MASK(MCDR2_ADDRESS)
. @1 I; y. A% c" y5 Z#define BIT_MASK(__bf) (((1U << (bw ## __bf)) - 1) << (bs ## __bf))
9 ~7 r) T; H! T) B/ E/ ^#define SET_BITS(__dst, __bf, __val) \; J, L/ J% Z5 p, s1 s: r& \
((__dst) = ((__dst) & ~(BIT_MASK(__bf))) | \
/ ^( ]9 s0 P1 D. _(((__val) << (bs ## __bf)) & (BIT_MASK(__bf))))
* s- L& I) L. @* @/ N8 O
3 A. X: _- ]3 p# y% C1 fSET_BITS(MCDR2, MCDR2_ADDRESS, RegisterNumber);( E) g1 u9 n, `- y* t
0 \+ o- W9 z! z( ]) k3 b
  函数和宏函数的区别就在于,宏函数占用了大量的空间,而函数占用了时间。大家要知道的是,函数调用是要使用系统的栈来保存数据的,如果编译器里有栈检查选项,一般在函数的头会嵌入一些汇编语句对当前栈进行检查;同时,CPU也要在函数调用时保存和恢复当前的现场,进行压栈和弹栈操作,所以,函数调用需要一些CPU时间。而宏函数不存在这个问题。宏函数仅仅作为预先写好的代码嵌入到当前程序,不会产生函数调用,所以仅仅是占用了空间,在频繁调用同一个宏函数的时候,该现象尤其突出。
' Q7 [& N) z. j  K: I! ]& _9 z% x) `( T
  D方法是我看到的最好的置位操作函数,是ARM公司源码的一部分,在短短的三行内实现了很多功能,几乎涵盖了所有的位操作功能。C方法是其变体,其中滋味还需大家仔细体会。# H: ?4 t5 J0 `: X% N; w1 f

, s9 p1 c0 s% P第2招:数学方法解决问题# o; n: T: a% x6 }' I, q) b
- F9 }. k" D2 H" n
  现在我们演绎高效C语言编写的第二招——采用数学方法来解决问题。; M9 [8 q. j. i. K2 D% }
+ |8 P4 Z% ^7 C; _, s4 d  A; j
  数学是计算机之母,没有数学的依据和基础,就没有计算机的发展,所以在编写程序的时候,采用一些数学方法会对程序的执行效率有数量级的提高。
3 ?+ m2 a/ x, h5 z( Y; m7 ~举例如下,求 1~100的和。
4 |2 {7 t9 e2 G7 o3 |方法E
0 ^4 L. d) i  Q( R* y. O' iint I , j;: B  f& N+ F0 O0 J4 W
for (I = 1 ;I<=100; I ++){/ Q# H# Q$ w+ @8 _% x! ^
j += I;
1 X3 D3 j/ [/ y7 y- o" ~  ^/ D}
0 G; c8 i! j; ^. H* p: n% y% ]( D方法F$ {4 \2 N& ~" ?/ ^  F4 I
int I;
! T% U8 |5 a9 |' }I = (100 * (1+100)) / 24 c8 V' }5 ^. k" `- }

9 F9 q' H" M& _& {" s% y( X' Z  这个例子是我印象最深的一个数学用例,是我的计算机启蒙老师考我的。当时我只有小学三年级,可惜我当时不知道用公式 N×(N+1)/ 2 来解决这个问题。方法E循环了100次才解决问题,也就是说最少用了100个赋值,100个判断,200个加法(I和j);而方法F仅仅用了1个加法,1 次乘法,1次除法。效果自然不言而喻。所以,现在我在编程序的时候,更多的是动脑筋找规律,最大限度地发挥数学的威力来提高程序运行的效率。0 w( n0 x; }! V! L0 ?) [. @9 s6 e

5 `1 k- i! Y+ R- Y2 x  A第3招:使用位操作7 l2 f/ m, T9 _2 [; L2 X# }
$ s9 W2 P' e* d; T
  实现高效的C语言编写的第三招——使用位操作,减少除法和取模的运算。
. t  O/ X. s% z6 k- n7 p( `  y! A
8 V6 d) `% b! Y7 I  在计算机程序中,数据的位是可以操作的最小数据单位,理论上可以用“位运算”来完成所有的运算和操作。一般的位操作是用来控制硬件的,或者做数据变换使用,但是,灵活的位操作可以有效地提高程序运行的效率。举例如下:7 q/ d8 B, y; ~8 ~5 `5 H
方法G
4 O" a7 K9 @: \1 A0 l7 Fint I,J;
, C* m+ @0 q  {" [I = 257 /8;
0 {6 y$ Z; ^8 ?* g) eJ = 456 % 32;
9 ?9 ?/ ^( C3 x1 I2 v+ }0 k# K方法H; r' x( f5 o. d% P* F2 t1 r
int I,J;
8 P' S$ o% o4 oI = 257 >>3;# x- c# Z( t" K/ M% e( P
J = 456 - (456 >> 4 << 4);
. l7 l9 ^+ G6 r/ y7 H+ O$ I. D8 o9 i2 z" A% i
  在字面上好像H比G麻烦了好多,但是,仔细查看产生的汇编代码就会明白,方法G调用了基本的取模函数和除法函数,既有函数调用,还有很多汇编代码和寄存器参与运算;而方法H则仅仅是几句相关的汇编,代码更简洁,效率更高。当然,由于编译器的不同,可能效率的差距不大,但是,以我目前遇到的MS C ,ARM C 来看,效率的差距还是不小。相关汇编代码就不在这里列举了。
+ H" s; O/ @% x2 ]+ I( z运用这招需要注意的是,因为CPU的不同而产生的问题。比如说,在PC上用这招编写的程序,并在PC上调试通过,在移植到一个16位机平台上的时候,可能会产生代码隐患。所以只有在一定技术进阶的基础下才可以使用这招。
  v6 O' h9 ^& @3 \$ D% }2 W' l: ^1 J" N4 l, b
第4招:汇编嵌入  r5 u- r- g5 O( O) w( K

9 ]$ |9 K) o! J4 O( c0 a' h0 I  W  高效C语言编程的必杀技,第四招——嵌入汇编。
3 Q6 @2 |3 C$ j, r
+ t( }# B( V; D, z3 n0 s: p6 g. M- s  “在熟悉汇编语言的人眼里,C语言编写的程序都是垃圾”。这种说法虽然偏激了一些,但是却有它的道理。汇编语言是效率最高的计算机语言,但是,不可能靠着它来写一个操作系统吧?所以,为了获得程序的高效率,我们只好采用变通的方法 ——嵌入汇编,混合编程。) P4 X8 z% l5 ?' a

4 A* }7 }* ~& U+ N! {& @  举例如下,将数组一赋值给数组二,要求每一字节都相符。% n" h8 w" e0 f$ T; F, i! J5 A% Z6 o
char string1[1024],string2[1024];
; `" j; \2 _: L! E9 P! }+ D方法I
' z$ b' p! G  y* M0 _6 Jint I;
+ y. x& W8 Z7 j6 gfor (I =0 ;I<1024;I++)( l7 P' J) q8 ]; c
*(string2 + I) = *(string1 + I)# j1 @" C% I/ [4 a: n0 L/ T- F
方法J! ~" c9 X! r; n1 b
#ifdef _PC_. \& Z% L9 c3 |
int I;: f' D. D6 q3 z4 Q& I9 l3 _
for (I =0 ;I<1024;I++)& R7 l& T) J' K% d
*(string2 + I) = *(string1 + I);4 ?5 L( \+ I; I5 b
#else; t' |0 Z# {3 Q% E2 i
#ifdef _ARM_
+ e: z- H* Y6 i( w, }. h3 D__asm# c/ W, k) S- _9 d* F
{
) G6 N$ S6 E7 ^* DMOV R0,string1* L+ j/ i: L: u' {1 C' S
MOV R1,string2
& i6 C5 b% m" w0 eMOV R2,#0
7 ~1 V; _9 \; G3 z8 l* D1 E; f9 g* Uloop:" _1 a* ]. W9 w; V* \/ _( U
LDMIA R0!, [R3-R11]
. U8 d9 Z! N6 y$ tSTMIA R1!, [R3-R11]
) T  R/ d( J  q; @  HADD R2,R2,#8+ l- g  R- N8 B+ F4 {
CMP R2, #4009 P: e3 f1 q8 E7 ?  |: x# `
BNE loop
  `2 B) i% o/ o! I}; v1 s" k! Y/ [2 m
#endif: X7 ~% a0 l" r/ U. X
' u. g* A* \* }4 N* @6 N( g
  方法I是最常见的方法,使用了1024次循环;方法J则根据平台不同做了区分,在ARM平台下,用嵌入汇编仅用128次循环就完成了同样的操作。这里有朋友会说,为什么不用标准的内存拷贝函数呢?这是因为在源数据里可能含有数据为0的字节,这样的话,标准库函数会提前结束而不会完成我们要求的操作。这个例程典型应用于LCD数据的拷贝过程。根据不同的CPU,熟练使用相应的嵌入汇编,可以大大提高程序执行的效率。4 R$ E7 Y  ]' C( ]; z% H* G4 _. S
2 D) s% u4 y% g# @% R8 K, m: B& f
  虽然是必杀技,但是如果轻易使用会付出惨重的代价。这是因为,使用了嵌入汇编,便限制了程序的可移植性,使程序在不同平台移植的过程中,卧虎藏龙,险象环生!同时该招数也与现代软件工程的思想相违背,只有在迫不得已的情况下才可以采用。切记,切记。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|小黑屋|宁德市腾云网络科技有限公司 ( 闽ICP备2022007940号-5|闽公网安备 35092202000206号 )

GMT+8, 2025-11-14 23:40 , Processed in 0.017259 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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