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

C语言高效编程的几招

[复制链接]
发表于 2006-11-14 15:25:09 | 显示全部楼层 |阅读模式
引言:0 u9 Z$ S% h& m# N. J+ D
  编写高效简洁的C语言代码,是许多软件工程师追求的目标。本文就工作中的一些体会和经验做相关的阐述,不对的地方请各位指教。
" ^% B5 V( v4 [2 |" P7 o. O+ G- O& x4 Y
第1招:以空间换时间+ v5 {5 }, S% U7 Z, b- m% C

) b' R0 {& K& h  计算机程序中最大的矛盾是空间和时间的矛盾,那么,从这个角度出发逆向思维来考虑程序的效率问题,我们就有了解决问题的第1招——以空间换时间。
* j9 d: Q; ^& X& Q5 s$ T例如:字符串的赋值。
7 {. f) f) ^, i) j; f方法A,通常的办法:
1 ~: }$ V( y" v#define LEN 32
2 _7 B) ~5 E) S/ R% cchar string1 [LEN];
6 g, w3 _9 C! v9 Umemset (string1,0,LEN);
8 p$ ?. ~3 D: K1 k5 Mstrcpy (string1,“This is a example!!”);( [. X8 e4 y( w2 u2 M/ ?
方法B:3 K- H( h: s5 U! ?* |7 ]
const char string2[LEN] =“This is a example!”;9 I) M' v$ R% b! c: g
char * cp;/ h' Y1 @: s( L$ L; J* g3 z
cp = string2 ;+ K& _8 H; F3 h
(使用的时候可以直接用指针来操作。)8 U; P1 [4 v- D; m& r: m. Q8 C

8 v, H& Z$ D+ b: h: x  从上面的例子可以看出,A和B的效率是不能比的。在同样的存储空间下,B直接使用指针就可以操作了,而A需要调用两个字符函数才能完成。B的缺点在于灵活性没有A好。在需要频繁更改一个字符串内容的时候,A具有更好的灵活性;如果采用方法B,则需要预存许多字符串,虽然占用了大量的内存,但是获得了程序执行的高效率。! r9 K1 d; ~' K0 b5 H# K4 K
. ~+ B5 C) Q2 N4 H( ^
  如果系统的实时性要求很高,内存还有一些,那我推荐你使用该招数。% Y+ Z& u5 u7 N8 U0 G6 U1 @

, S9 d1 T/ ~+ H+ {: {& ]  该招数的变招——使用宏函数而不是函数。举例如下:
/ \; I4 Y$ I. G" e方法C:. W6 i) r" j3 y" ^# F2 U1 G6 L  O
#define bwMCDR2_ADDRESS 4
7 v: u3 h3 r& ]; w$ \% W#define bsMCDR2_ADDRESS 17
, I$ \+ e7 d) y& ]* Gint BIT_MASK(int __bf)7 U) K$ F& `2 v6 p8 l7 g, G3 F
{
# p. ^( |; ~) @6 U2 Rreturn ((1U << (bw ## __bf)) - 1) << (bs ## __bf);2 m- U' x0 P7 w7 c5 r1 O
}% ?3 Y  Q& G! |" o  H
void SET_BITS(int __dst, int __bf, int __val)
. w. ~- I) V# u: N- ]+ e. N: D1 n{+ S; b: W$ `" H
__dst = ((__dst) & ~(BIT_MASK(__bf))) | \
+ O2 h" e7 c/ f/ Z" i' R* \(((__val) << (bs ## __bf)) & (BIT_MASK(__bf))))/ [" c0 M9 i3 F* z: H
}& ^6 |9 k( I. V! E, S. k. x& N, F

5 Z  [) K0 k! Y! U/ {! zSET_BITS(MCDR2, MCDR2_ADDRESS, RegisterNumber);7 i5 Q; G) e" n
方法D:
6 h, q  `1 x/ J7 u# F#define bwMCDR2_ADDRESS 41 C2 R) ^+ J0 N2 p% P3 ]! ]) Y; A
#define bsMCDR2_ADDRESS 17$ M% ~5 r1 ~2 n! S
#define bmMCDR2_ADDRESS BIT_MASK(MCDR2_ADDRESS)
5 m# H+ e, S' g' I- v2 o4 z#define BIT_MASK(__bf) (((1U << (bw ## __bf)) - 1) << (bs ## __bf))5 o; P& O8 e2 t5 f( B, N. ^
#define SET_BITS(__dst, __bf, __val) \
/ H4 \+ ]$ D1 u4 F- z' O" @((__dst) = ((__dst) & ~(BIT_MASK(__bf))) | \
& e0 }: g4 J6 f1 F(((__val) << (bs ## __bf)) & (BIT_MASK(__bf))))# t/ X7 M! B7 ~
! U% e% `: V" v, L& Z$ T% y
SET_BITS(MCDR2, MCDR2_ADDRESS, RegisterNumber);
/ H3 H% N# J# j  y2 ]- W$ N' d5 B) R  j
  函数和宏函数的区别就在于,宏函数占用了大量的空间,而函数占用了时间。大家要知道的是,函数调用是要使用系统的栈来保存数据的,如果编译器里有栈检查选项,一般在函数的头会嵌入一些汇编语句对当前栈进行检查;同时,CPU也要在函数调用时保存和恢复当前的现场,进行压栈和弹栈操作,所以,函数调用需要一些CPU时间。而宏函数不存在这个问题。宏函数仅仅作为预先写好的代码嵌入到当前程序,不会产生函数调用,所以仅仅是占用了空间,在频繁调用同一个宏函数的时候,该现象尤其突出。
' k9 `% `5 f7 f
; K% b) C, @( [3 Q  D方法是我看到的最好的置位操作函数,是ARM公司源码的一部分,在短短的三行内实现了很多功能,几乎涵盖了所有的位操作功能。C方法是其变体,其中滋味还需大家仔细体会。
  X! n7 K- ]8 z, v* j! F2 g) Y: r
7 w5 `0 R2 {- _. n7 ~4 v2 A2 I第2招:数学方法解决问题
1 F2 e4 p5 U# N8 A8 d" i
4 A7 p; V9 [6 a0 x  现在我们演绎高效C语言编写的第二招——采用数学方法来解决问题。5 b) h6 r$ `7 K1 f  e2 L
9 B/ v* x: ^, x1 N
  数学是计算机之母,没有数学的依据和基础,就没有计算机的发展,所以在编写程序的时候,采用一些数学方法会对程序的执行效率有数量级的提高。
6 ^3 J1 Y& w7 P! s* `% Y% F举例如下,求 1~100的和。
' T$ H- ?' b2 R+ Q* r9 Q方法E/ P% s5 ^: [8 P, v
int I , j;
; B7 ^! O" v5 Efor (I = 1 ;I<=100; I ++){
5 y% I1 }2 X% J0 p  |, nj += I;
# l6 K" L* w- S9 W! P}
& K2 H3 X; n; I方法F
5 M2 Y, I* d4 wint I;- W- R2 z( Z; t
I = (100 * (1+100)) / 2/ a2 @$ q3 N$ Z  r( I4 k
) g3 z& K% B& X$ E" }) F  t
  这个例子是我印象最深的一个数学用例,是我的计算机启蒙老师考我的。当时我只有小学三年级,可惜我当时不知道用公式 N×(N+1)/ 2 来解决这个问题。方法E循环了100次才解决问题,也就是说最少用了100个赋值,100个判断,200个加法(I和j);而方法F仅仅用了1个加法,1 次乘法,1次除法。效果自然不言而喻。所以,现在我在编程序的时候,更多的是动脑筋找规律,最大限度地发挥数学的威力来提高程序运行的效率。; E$ L/ }8 \; I5 @# i% b) H8 O* N
' v/ q& U; ~; _, Q& O
第3招:使用位操作2 U# E& v- P9 Z5 u

4 V4 K: F; D0 ~2 m+ x  实现高效的C语言编写的第三招——使用位操作,减少除法和取模的运算。2 g& n+ o& Y0 I4 \

+ b7 O* W, H0 K- H$ F: I  在计算机程序中,数据的位是可以操作的最小数据单位,理论上可以用“位运算”来完成所有的运算和操作。一般的位操作是用来控制硬件的,或者做数据变换使用,但是,灵活的位操作可以有效地提高程序运行的效率。举例如下:
; c: H# m3 S, f- l$ S1 R) \方法G
3 f1 F2 m# s0 }( ]3 G8 }& Uint I,J;
+ }# N& k$ p' [$ p. G4 p3 aI = 257 /8;" [/ ~% R" c' i3 S$ Z, r8 D& a+ _# W6 {
J = 456 % 32;
9 Q  m7 |# Q, K( b  y) D方法H" n0 F& t* I; U9 z6 J
int I,J;
0 V! V2 i4 a9 v+ BI = 257 >>3;# |+ y- |4 Q( I0 Z# h0 }
J = 456 - (456 >> 4 << 4);
" W) \$ l, v3 M0 q# z% {
1 c# {4 r! C7 _2 V0 Z% K  在字面上好像H比G麻烦了好多,但是,仔细查看产生的汇编代码就会明白,方法G调用了基本的取模函数和除法函数,既有函数调用,还有很多汇编代码和寄存器参与运算;而方法H则仅仅是几句相关的汇编,代码更简洁,效率更高。当然,由于编译器的不同,可能效率的差距不大,但是,以我目前遇到的MS C ,ARM C 来看,效率的差距还是不小。相关汇编代码就不在这里列举了。
4 B: q% X' S& I3 j0 z运用这招需要注意的是,因为CPU的不同而产生的问题。比如说,在PC上用这招编写的程序,并在PC上调试通过,在移植到一个16位机平台上的时候,可能会产生代码隐患。所以只有在一定技术进阶的基础下才可以使用这招。
; z- q5 X% V5 B+ O4 Y  J$ i, }- z8 ?/ ^
第4招:汇编嵌入
1 F5 a( s7 U% [5 {9 q. W% E) U: E5 X. g" I7 {3 _5 K( @
  高效C语言编程的必杀技,第四招——嵌入汇编。
* X( A" e5 n+ ~% t) d9 P9 X- f) |
  “在熟悉汇编语言的人眼里,C语言编写的程序都是垃圾”。这种说法虽然偏激了一些,但是却有它的道理。汇编语言是效率最高的计算机语言,但是,不可能靠着它来写一个操作系统吧?所以,为了获得程序的高效率,我们只好采用变通的方法 ——嵌入汇编,混合编程。
8 C1 A8 T; p3 i
0 [( c$ A% M& C2 Y2 p  举例如下,将数组一赋值给数组二,要求每一字节都相符。
/ c- H# H6 Y0 M1 V2 [1 O1 D7 ~( ?char string1[1024],string2[1024];. o( ~5 t% T( s. I
方法I
/ D: x, n  j! G% T0 j, Bint I;
2 {& ], D* r" A  e$ m7 l, P3 zfor (I =0 ;I<1024;I++)8 G# l& d# x( ^" p8 O! p4 B% x5 l
*(string2 + I) = *(string1 + I)/ p+ E6 v! i* l* H
方法J* w- S/ ?1 m& E* `
#ifdef _PC_
  ]/ c- x9 }3 B9 Y" a4 eint I;
- {1 A: ^& h" h! [! x( N: p% R0 A& [for (I =0 ;I<1024;I++)) T; u/ q& J2 t
*(string2 + I) = *(string1 + I);# ^; K' |# d+ u4 t
#else" s0 ~8 J" X% N+ T
#ifdef _ARM_
& D, ?/ d7 H& }: m8 p8 y" Q! a__asm
4 F( |& [+ E& G0 y1 t  W# Z{7 q" w) l) z0 [2 a1 X8 ^
MOV R0,string1
/ I/ Q2 |& D4 y& wMOV R1,string2
! L4 h2 U+ Y' O0 R( YMOV R2,#0! a; e" P. w7 D3 L
loop:  o9 h3 `; u) y" J
LDMIA R0!, [R3-R11]
# n0 i! v* K! r% @STMIA R1!, [R3-R11]
/ s' Q$ E+ {/ W4 b0 A# ]  sADD R2,R2,#8
5 K! s! I8 e1 P, v5 ~0 ?CMP R2, #400
- H3 I/ Y. P  u; @7 L/ Z( ?! sBNE loop6 F6 e  w9 W5 v+ ^* `7 k( Z
}; I% v" }0 f# J' P# C2 p3 J
#endif. [$ ^9 q$ k4 D

+ k0 b3 P* N  G9 C, f  方法I是最常见的方法,使用了1024次循环;方法J则根据平台不同做了区分,在ARM平台下,用嵌入汇编仅用128次循环就完成了同样的操作。这里有朋友会说,为什么不用标准的内存拷贝函数呢?这是因为在源数据里可能含有数据为0的字节,这样的话,标准库函数会提前结束而不会完成我们要求的操作。这个例程典型应用于LCD数据的拷贝过程。根据不同的CPU,熟练使用相应的嵌入汇编,可以大大提高程序执行的效率。
# f, G  d8 M8 {, T7 {5 \- m3 N3 q/ F% q% P) i# D4 ]
  虽然是必杀技,但是如果轻易使用会付出惨重的代价。这是因为,使用了嵌入汇编,便限制了程序的可移植性,使程序在不同平台移植的过程中,卧虎藏龙,险象环生!同时该招数也与现代软件工程的思想相违背,只有在迫不得已的情况下才可以采用。切记,切记。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-6-18 10:21 , Processed in 0.017212 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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