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

C语言高效编程的几招

[复制链接]
发表于 2006-11-14 15:25:09 | 显示全部楼层 |阅读模式
引言:
! y5 q" a0 A4 W- G  编写高效简洁的C语言代码,是许多软件工程师追求的目标。本文就工作中的一些体会和经验做相关的阐述,不对的地方请各位指教。6 }$ L9 U0 ~# v" H5 ~4 i+ V

0 S# t/ J$ w( d% Y- i第1招:以空间换时间
/ Q2 g9 I0 L: v7 v; E4 g& X% p5 s' G6 P  S& n- X1 l2 D: r1 W
  计算机程序中最大的矛盾是空间和时间的矛盾,那么,从这个角度出发逆向思维来考虑程序的效率问题,我们就有了解决问题的第1招——以空间换时间。
2 v' g1 ^3 t8 E( Z" B例如:字符串的赋值。% z, h, w" F/ }# t
方法A,通常的办法:  k1 w1 O% x, |
#define LEN 32/ l. u; D6 T3 D2 ?- ?' q% ^
char string1 [LEN];
. C  Q$ P2 g5 n. y$ d  S  l+ A6 Ymemset (string1,0,LEN);
2 e! ]$ O! @; a+ y- c/ {strcpy (string1,“This is a example!!”);
+ A1 s* _# Z  \# o6 I' p方法B:, D0 S  w' M# J/ g7 D
const char string2[LEN] =“This is a example!”;
7 T' ?' n3 Z% C' j- g+ Wchar * cp;
4 c5 t% `1 i- z- h; [5 V: L( qcp = string2 ;1 H0 P/ E6 D; z
(使用的时候可以直接用指针来操作。)
4 U) _. k5 O! K: P/ [, E/ P/ N# p/ O* }, Z0 W
  从上面的例子可以看出,A和B的效率是不能比的。在同样的存储空间下,B直接使用指针就可以操作了,而A需要调用两个字符函数才能完成。B的缺点在于灵活性没有A好。在需要频繁更改一个字符串内容的时候,A具有更好的灵活性;如果采用方法B,则需要预存许多字符串,虽然占用了大量的内存,但是获得了程序执行的高效率。0 `1 y7 N2 X& R0 l/ X

0 Q* b8 E9 K& b+ v% f7 u  如果系统的实时性要求很高,内存还有一些,那我推荐你使用该招数。
+ f6 G! {" @$ s1 Z! d, D  d  y9 r. E4 D3 ]
  该招数的变招——使用宏函数而不是函数。举例如下:- e* i0 f2 k% \3 }
方法C:
9 T; V9 k7 o" J- T0 d#define bwMCDR2_ADDRESS 4
) T  s! t( d( }5 p9 S& J4 q% z#define bsMCDR2_ADDRESS 17
  F  r7 v$ ~; C, ?int BIT_MASK(int __bf)6 p& ?1 [  n/ L- z/ Z) G
{
3 B6 v2 ~# z. B4 E; Areturn ((1U << (bw ## __bf)) - 1) << (bs ## __bf);
* p+ t2 Q* G5 p& z; Y8 U}; ~0 G/ E  p9 R/ C
void SET_BITS(int __dst, int __bf, int __val)
' F8 ]  z6 e/ \9 C$ i0 `{8 b/ |/ H, F+ W1 @9 }6 j, T
__dst = ((__dst) & ~(BIT_MASK(__bf))) | \
3 W& u" v% j3 g$ O9 _8 _(((__val) << (bs ## __bf)) & (BIT_MASK(__bf))))3 ]+ j# n% V% \: s5 B- W
}: e; l/ N: z" m- j4 f; [
4 C9 g/ f# c. O% B7 m. L
SET_BITS(MCDR2, MCDR2_ADDRESS, RegisterNumber);; ?4 B# Z4 w$ H; q/ E3 g) D: {
方法D:
; G; l8 a. L, M( e, F#define bwMCDR2_ADDRESS 46 n0 K4 i* G7 E9 E+ a) }  E) ?
#define bsMCDR2_ADDRESS 17
) R& }( D/ o' {8 Y; N#define bmMCDR2_ADDRESS BIT_MASK(MCDR2_ADDRESS), B' u8 m- A* C6 @5 S" S% E, h4 j9 ]3 I
#define BIT_MASK(__bf) (((1U << (bw ## __bf)) - 1) << (bs ## __bf))0 j7 e. ]! R$ w: m9 R( {" G$ t* @
#define SET_BITS(__dst, __bf, __val) \* n3 w/ m- T4 G. q! M1 G$ S9 h
((__dst) = ((__dst) & ~(BIT_MASK(__bf))) | \! r0 G; ^# o" B# }( O/ r8 d! Q- e) ?
(((__val) << (bs ## __bf)) & (BIT_MASK(__bf))))
: v7 W) h$ B, T* I! t
% s6 S5 I9 b/ N. N: H: @; aSET_BITS(MCDR2, MCDR2_ADDRESS, RegisterNumber);$ p5 F3 o/ ]5 U5 i
& Y  D9 y5 H# [
  函数和宏函数的区别就在于,宏函数占用了大量的空间,而函数占用了时间。大家要知道的是,函数调用是要使用系统的栈来保存数据的,如果编译器里有栈检查选项,一般在函数的头会嵌入一些汇编语句对当前栈进行检查;同时,CPU也要在函数调用时保存和恢复当前的现场,进行压栈和弹栈操作,所以,函数调用需要一些CPU时间。而宏函数不存在这个问题。宏函数仅仅作为预先写好的代码嵌入到当前程序,不会产生函数调用,所以仅仅是占用了空间,在频繁调用同一个宏函数的时候,该现象尤其突出。
/ [6 \; u+ U2 L1 o& Q) \0 e, M2 Y' @, p
  D方法是我看到的最好的置位操作函数,是ARM公司源码的一部分,在短短的三行内实现了很多功能,几乎涵盖了所有的位操作功能。C方法是其变体,其中滋味还需大家仔细体会。! U. H6 M4 q* E+ f$ d3 b
1 m0 W4 N  R5 U  N
第2招:数学方法解决问题1 |6 p2 ^# s% K. k( e
1 r/ m* j* I% ~0 S. E3 ^7 G8 g
  现在我们演绎高效C语言编写的第二招——采用数学方法来解决问题。
! p# k& T# H3 C+ F9 H! j  F5 O: D9 Z" F
  数学是计算机之母,没有数学的依据和基础,就没有计算机的发展,所以在编写程序的时候,采用一些数学方法会对程序的执行效率有数量级的提高。2 {. S3 D" f; S! E: `0 G
举例如下,求 1~100的和。
* F: B, h" R/ e4 G) h方法E
8 M) T5 v3 V% G( Dint I , j;
6 ]6 w1 S4 f" ]: q6 `( ^, rfor (I = 1 ;I<=100; I ++){
! U6 q1 H3 y9 r6 Ej += I;
6 X* r6 L" R. x; R7 U" R" b2 Q. A}
! v. ^! ]" K* K1 R方法F
2 N7 d9 D3 L0 x* Pint I;% X, ^; `9 |& R$ a2 V3 `
I = (100 * (1+100)) / 2+ o; e5 J' D, n" e- J: w
" y- i+ q& z. H5 _% o* A
  这个例子是我印象最深的一个数学用例,是我的计算机启蒙老师考我的。当时我只有小学三年级,可惜我当时不知道用公式 N×(N+1)/ 2 来解决这个问题。方法E循环了100次才解决问题,也就是说最少用了100个赋值,100个判断,200个加法(I和j);而方法F仅仅用了1个加法,1 次乘法,1次除法。效果自然不言而喻。所以,现在我在编程序的时候,更多的是动脑筋找规律,最大限度地发挥数学的威力来提高程序运行的效率。
) }) V5 u# z" j- ^8 O
* D3 {+ \) ^) z) x2 z1 I) @第3招:使用位操作
& z% Y# O7 L- P! L' F$ G) Y0 m0 \2 e2 D$ u* X- p" X. I! r' D& m
  实现高效的C语言编写的第三招——使用位操作,减少除法和取模的运算。
9 @/ P: f% e) P" Y5 n$ N; e! N" G; t7 F& p% B* F
  在计算机程序中,数据的位是可以操作的最小数据单位,理论上可以用“位运算”来完成所有的运算和操作。一般的位操作是用来控制硬件的,或者做数据变换使用,但是,灵活的位操作可以有效地提高程序运行的效率。举例如下:2 a6 Y( q+ ~* K* P6 U2 N  j
方法G- A& F" M0 u3 I6 z
int I,J;% v* i$ e4 r0 L4 @! ]. M* D  o, r, K) o
I = 257 /8;1 b* X) p6 \) O  Y1 L& O$ H7 F
J = 456 % 32;4 M) B8 F& V8 n/ F" q3 |! X, F
方法H
9 V! V5 j' r. A1 Q: p8 f& ]int I,J;2 R) O: Y+ D; \9 B# |7 R
I = 257 >>3;
3 f( E! t8 f/ {: Y& [  @+ Z/ f; ?J = 456 - (456 >> 4 << 4);
3 P, S; h0 \3 \6 a- }! d" ^6 Z, P; w9 u' p
  在字面上好像H比G麻烦了好多,但是,仔细查看产生的汇编代码就会明白,方法G调用了基本的取模函数和除法函数,既有函数调用,还有很多汇编代码和寄存器参与运算;而方法H则仅仅是几句相关的汇编,代码更简洁,效率更高。当然,由于编译器的不同,可能效率的差距不大,但是,以我目前遇到的MS C ,ARM C 来看,效率的差距还是不小。相关汇编代码就不在这里列举了。1 s. U7 @$ I  U$ @( M+ A
运用这招需要注意的是,因为CPU的不同而产生的问题。比如说,在PC上用这招编写的程序,并在PC上调试通过,在移植到一个16位机平台上的时候,可能会产生代码隐患。所以只有在一定技术进阶的基础下才可以使用这招。
! m; O3 o; F6 ]5 H' Q
4 f* E4 ^: v; P  d: s+ W/ F第4招:汇编嵌入0 F: E6 Z; C. S. [6 B( R& Z$ b- y8 a+ K

+ O5 ^2 z2 G) f" G: k  高效C语言编程的必杀技,第四招——嵌入汇编。6 d; b# }* }' T, q, ^  k, p3 \

' e! u, p8 U( i" e1 M; J" x* s  “在熟悉汇编语言的人眼里,C语言编写的程序都是垃圾”。这种说法虽然偏激了一些,但是却有它的道理。汇编语言是效率最高的计算机语言,但是,不可能靠着它来写一个操作系统吧?所以,为了获得程序的高效率,我们只好采用变通的方法 ——嵌入汇编,混合编程。! t; g7 R7 J  F

' J: d: Z. J! t9 ~  举例如下,将数组一赋值给数组二,要求每一字节都相符。
0 O4 d' w+ X# ^+ R9 Schar string1[1024],string2[1024];6 S  g. F/ E( c! Q! t8 l' M
方法I' m/ g& A" N0 _2 o. a
int I;
( G+ \+ i3 L- n9 G8 b  q" _( B4 _4 @for (I =0 ;I<1024;I++)
3 Q4 ~/ u/ k# t" M*(string2 + I) = *(string1 + I)
: q4 o, K9 h/ P, F# @4 P方法J
: f3 x5 ]  a& \( U" Q#ifdef _PC_
/ B: K4 b" L' Y& r2 O& A- lint I;
+ p" w! R% K  i+ yfor (I =0 ;I<1024;I++)" {! ~. z" z( w4 _$ G# Q5 W/ `
*(string2 + I) = *(string1 + I);
1 D: m8 w! p2 g! ?# u# U#else
* u( k3 p: h9 D0 [) [5 t#ifdef _ARM_" \; g& y/ p% w3 W+ L* R3 b
__asm
; j4 R7 d! ~% C8 S7 e9 t( U{2 m* s# i5 \9 E% i
MOV R0,string16 s! L5 d. o* M; \8 m
MOV R1,string2
7 ^9 F. o+ q' E5 ~( HMOV R2,#0
1 T7 _. Q; s4 o) [1 x# R! S* Vloop:' L) o7 c# `. b# _. f5 z
LDMIA R0!, [R3-R11]$ k. o) X; X  H. Q
STMIA R1!, [R3-R11]
2 J% X0 B! c% d, `ADD R2,R2,#8; Z9 X. k: D& j1 l
CMP R2, #400* W' |+ u) N* T# Y' R
BNE loop
) i3 a9 q% h0 A) p  m: F. i}. ?+ ?/ I: y  g. k
#endif  M$ ]: I! o+ X- n0 z9 F( l- h
* {9 @8 G9 f2 o9 C9 O
  方法I是最常见的方法,使用了1024次循环;方法J则根据平台不同做了区分,在ARM平台下,用嵌入汇编仅用128次循环就完成了同样的操作。这里有朋友会说,为什么不用标准的内存拷贝函数呢?这是因为在源数据里可能含有数据为0的字节,这样的话,标准库函数会提前结束而不会完成我们要求的操作。这个例程典型应用于LCD数据的拷贝过程。根据不同的CPU,熟练使用相应的嵌入汇编,可以大大提高程序执行的效率。
2 \2 H  d5 l+ D1 g
4 K7 d$ ?' S4 B- }/ w1 ~  虽然是必杀技,但是如果轻易使用会付出惨重的代价。这是因为,使用了嵌入汇编,便限制了程序的可移植性,使程序在不同平台移植的过程中,卧虎藏龙,险象环生!同时该招数也与现代软件工程的思想相违背,只有在迫不得已的情况下才可以采用。切记,切记。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-6-18 09:14 , Processed in 0.018042 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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