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

C语言高效编程的几招

[复制链接]
发表于 2006-11-14 15:25:09 | 显示全部楼层 |阅读模式
引言:8 \* B; X+ r; g. k& C4 J
  编写高效简洁的C语言代码,是许多软件工程师追求的目标。本文就工作中的一些体会和经验做相关的阐述,不对的地方请各位指教。
( _2 r0 J: U3 x) G0 ^: Z# G
) [! ]8 k3 r6 h* h第1招:以空间换时间. s4 n) i- n1 @8 w9 x
  W% {/ M6 `  x4 R8 B
  计算机程序中最大的矛盾是空间和时间的矛盾,那么,从这个角度出发逆向思维来考虑程序的效率问题,我们就有了解决问题的第1招——以空间换时间。; [5 Z) B$ S' W/ F/ `
例如:字符串的赋值。( V. h1 [1 y# c
方法A,通常的办法:
; l+ Z1 O" j) {+ N#define LEN 32
4 N- z+ R2 ]' [8 v: Z# _char string1 [LEN];7 f/ r1 u7 F9 o
memset (string1,0,LEN);
: q6 O" c; n9 J5 P  l$ |strcpy (string1,“This is a example!!”);9 S% }$ s- m8 g( D5 h. c
方法B:
% }; f# i$ w9 d1 f, }- Iconst char string2[LEN] =“This is a example!”;8 \8 J& U3 f4 \1 y, I
char * cp;
  @) a/ l3 b) w, W: dcp = string2 ;
" N/ e1 E1 u% X! Q8 T# M(使用的时候可以直接用指针来操作。)/ q+ G8 A. q5 ?" l) m2 C
! ?( E" |) t3 @7 b2 |( f
  从上面的例子可以看出,A和B的效率是不能比的。在同样的存储空间下,B直接使用指针就可以操作了,而A需要调用两个字符函数才能完成。B的缺点在于灵活性没有A好。在需要频繁更改一个字符串内容的时候,A具有更好的灵活性;如果采用方法B,则需要预存许多字符串,虽然占用了大量的内存,但是获得了程序执行的高效率。/ y# U& D: R* S( f" K0 j  A& ?
$ F- M7 [* w# T! H$ @
  如果系统的实时性要求很高,内存还有一些,那我推荐你使用该招数。- K, ^) A, V0 w4 U  }. ^
  M3 B" q; X. t1 v  [
  该招数的变招——使用宏函数而不是函数。举例如下:: Q5 {7 w) l5 G5 p6 w! V; ?
方法C:/ a9 R+ q$ o/ x  D9 t, A% ^
#define bwMCDR2_ADDRESS 48 ^; A$ L1 B$ T- g* |
#define bsMCDR2_ADDRESS 17* \$ S- L+ Z1 R- _' Z
int BIT_MASK(int __bf)
0 \" ?* @8 V; A: u" y" |4 y{# N+ T$ G/ ?: _6 d1 y/ O
return ((1U << (bw ## __bf)) - 1) << (bs ## __bf);
0 m1 D7 N0 n2 k* ]$ `4 [}
2 b' G3 w, `1 U4 g! g0 D& Avoid SET_BITS(int __dst, int __bf, int __val)4 r9 l4 Q: Q0 O+ D) |
{
; \+ Y6 u7 u# }__dst = ((__dst) & ~(BIT_MASK(__bf))) | \7 N: U$ P/ l$ w) s$ H
(((__val) << (bs ## __bf)) & (BIT_MASK(__bf))))/ ?/ s9 y  O: K2 j2 N" g# p% i
}! A6 B- L8 q) W

+ x# j9 n8 A* a- Y5 a. pSET_BITS(MCDR2, MCDR2_ADDRESS, RegisterNumber);
5 y& E1 n6 l' K" ~# i方法D:( L0 z3 S( m& |) R  I
#define bwMCDR2_ADDRESS 4
' C' Y  A5 j% O  S6 B. y#define bsMCDR2_ADDRESS 17
6 ?2 P' n5 M) J5 W* P#define bmMCDR2_ADDRESS BIT_MASK(MCDR2_ADDRESS)
' s8 G! ^- ~$ `6 C#define BIT_MASK(__bf) (((1U << (bw ## __bf)) - 1) << (bs ## __bf))- \5 c' `* A" w, c" w3 n, ?0 H
#define SET_BITS(__dst, __bf, __val) \2 J1 L2 B" P; G$ j2 `
((__dst) = ((__dst) & ~(BIT_MASK(__bf))) | \
' ]: l1 y/ `/ T. \( R4 N7 z6 B(((__val) << (bs ## __bf)) & (BIT_MASK(__bf))))
4 i! A( }5 z6 A+ t+ @$ Q) X. v( R( k# J0 U8 B( o
SET_BITS(MCDR2, MCDR2_ADDRESS, RegisterNumber);% R3 x# x9 v: ~% {7 J0 z

/ c- f: }" U8 }- E( `" A, x  函数和宏函数的区别就在于,宏函数占用了大量的空间,而函数占用了时间。大家要知道的是,函数调用是要使用系统的栈来保存数据的,如果编译器里有栈检查选项,一般在函数的头会嵌入一些汇编语句对当前栈进行检查;同时,CPU也要在函数调用时保存和恢复当前的现场,进行压栈和弹栈操作,所以,函数调用需要一些CPU时间。而宏函数不存在这个问题。宏函数仅仅作为预先写好的代码嵌入到当前程序,不会产生函数调用,所以仅仅是占用了空间,在频繁调用同一个宏函数的时候,该现象尤其突出。
; r6 U' }  g' Z$ P' O& E+ o: J) I5 e  }8 W! @2 v
  D方法是我看到的最好的置位操作函数,是ARM公司源码的一部分,在短短的三行内实现了很多功能,几乎涵盖了所有的位操作功能。C方法是其变体,其中滋味还需大家仔细体会。
! M( m2 e0 G. {9 g9 a- n( v. w
8 b$ |) I/ ]9 }' K8 |第2招:数学方法解决问题
& \7 N) C; ]3 B; S. X5 q# a3 V( q
  现在我们演绎高效C语言编写的第二招——采用数学方法来解决问题。
+ Q$ T0 w& n4 f' {( `5 y+ T- W" i2 w! O2 @
  数学是计算机之母,没有数学的依据和基础,就没有计算机的发展,所以在编写程序的时候,采用一些数学方法会对程序的执行效率有数量级的提高。5 q* v# E$ t( I) m7 }
举例如下,求 1~100的和。5 H+ e# ?( h& H2 H0 M
方法E& m  _9 M/ s/ K
int I , j;* i+ q% D& d4 W0 c
for (I = 1 ;I<=100; I ++){
9 t' C: U/ i, z! X7 y. z) k- Cj += I;
/ k2 T- o, t0 b, W  f3 N2 O: ]}' n$ w) D9 R! P
方法F2 T7 b# x9 b2 x: C
int I;
5 r- A6 |2 {+ tI = (100 * (1+100)) / 25 L2 t  V& f* G7 d
7 F0 p6 }4 n/ J0 U, h! P4 N
  这个例子是我印象最深的一个数学用例,是我的计算机启蒙老师考我的。当时我只有小学三年级,可惜我当时不知道用公式 N×(N+1)/ 2 来解决这个问题。方法E循环了100次才解决问题,也就是说最少用了100个赋值,100个判断,200个加法(I和j);而方法F仅仅用了1个加法,1 次乘法,1次除法。效果自然不言而喻。所以,现在我在编程序的时候,更多的是动脑筋找规律,最大限度地发挥数学的威力来提高程序运行的效率。* y3 H0 \' r3 J7 L9 K* O
! k" V6 _# h0 K. Q4 ^
第3招:使用位操作3 m( X! k9 s: S. t0 Q; w3 I% u

- M2 I0 T+ X- b0 c  实现高效的C语言编写的第三招——使用位操作,减少除法和取模的运算。- q8 L% s7 w9 O- s/ t0 v( o
! a& z' [6 j# m8 j4 \1 l
  在计算机程序中,数据的位是可以操作的最小数据单位,理论上可以用“位运算”来完成所有的运算和操作。一般的位操作是用来控制硬件的,或者做数据变换使用,但是,灵活的位操作可以有效地提高程序运行的效率。举例如下:1 {6 ~  e! u7 _( k0 O
方法G
- ~% U8 N, d! |  ~2 J$ S& z& l* vint I,J;
: F+ p. B9 _# G% ?2 X0 cI = 257 /8;5 ^+ w- E9 j" z9 b5 _6 ]/ m
J = 456 % 32;
- c6 ~! D& s+ N9 {8 u! y! I方法H
" j: C# }& L) D  zint I,J;+ c7 o" K, `) c! n' O
I = 257 >>3;
( l; g: T. K6 p% n0 W2 s- e: HJ = 456 - (456 >> 4 << 4);
) Z& p$ a$ k- l6 W3 X+ `& |
+ Y7 m! o6 f1 o% i2 K" e# W9 [  在字面上好像H比G麻烦了好多,但是,仔细查看产生的汇编代码就会明白,方法G调用了基本的取模函数和除法函数,既有函数调用,还有很多汇编代码和寄存器参与运算;而方法H则仅仅是几句相关的汇编,代码更简洁,效率更高。当然,由于编译器的不同,可能效率的差距不大,但是,以我目前遇到的MS C ,ARM C 来看,效率的差距还是不小。相关汇编代码就不在这里列举了。* F  ^; C9 k3 V' O# o# n: K$ `
运用这招需要注意的是,因为CPU的不同而产生的问题。比如说,在PC上用这招编写的程序,并在PC上调试通过,在移植到一个16位机平台上的时候,可能会产生代码隐患。所以只有在一定技术进阶的基础下才可以使用这招。$ d7 O! C4 t  D* d" q

; q) H: N& R9 e第4招:汇编嵌入: T  Z$ C% M9 C1 T. y
, W9 {# G! k* A9 [' k/ C* v8 X& \
  高效C语言编程的必杀技,第四招——嵌入汇编。
3 W  r- h4 W) u/ c. z, W3 ?' `( t3 X4 l" q
  “在熟悉汇编语言的人眼里,C语言编写的程序都是垃圾”。这种说法虽然偏激了一些,但是却有它的道理。汇编语言是效率最高的计算机语言,但是,不可能靠着它来写一个操作系统吧?所以,为了获得程序的高效率,我们只好采用变通的方法 ——嵌入汇编,混合编程。+ l0 S) @5 d6 l- a9 S3 K5 `4 z
& F) H( m2 m) q+ @3 K+ b! G& h
  举例如下,将数组一赋值给数组二,要求每一字节都相符。
/ `% b2 F9 ~: Q" K2 p6 tchar string1[1024],string2[1024];3 Q% g: A2 ], `- U
方法I( v0 _, j( Z( v0 c( A5 y8 _% Q
int I;
/ y& [- f4 E# b! c9 A$ Cfor (I =0 ;I<1024;I++)
2 b3 P0 {; z0 F( G, _  @% D. K  I+ J*(string2 + I) = *(string1 + I)
% a  i8 b5 f- @  B4 e方法J) t7 U! v- ?* N- d
#ifdef _PC_
- X5 P; W/ J5 K' Iint I;0 L6 R! g3 D. j8 a+ Y, Q- E" o% U
for (I =0 ;I<1024;I++)) h7 A9 I4 f7 p8 E& S% U4 O: D
*(string2 + I) = *(string1 + I);+ F; z: x3 m- J6 n
#else. T' M& s; p% b3 O  H4 K+ {- ?
#ifdef _ARM_
+ j& l- a- \2 S, h__asm5 N* e) J0 [  G5 N/ w. H% k
{
2 V" l+ ?! F4 t+ cMOV R0,string1
. T1 G; C+ X! q3 |" f5 IMOV R1,string2: G( k; G* D- \( ]; r7 i
MOV R2,#06 s8 V9 R4 @. P! x
loop:" _: C$ W: \2 q1 O: b4 T
LDMIA R0!, [R3-R11]7 l) j- L" B! ^+ @6 m1 ?* w
STMIA R1!, [R3-R11]
: d1 l6 v+ I$ q5 fADD R2,R2,#84 |% a2 O. }! D  f3 v( n9 a
CMP R2, #4005 q4 y# E! J6 K% w
BNE loop
- Z( @  h; _" v+ u}3 @3 `8 }- y! {
#endif
/ `' y" M' M" O, P- h$ V1 `8 d6 }& U' b4 `0 {
  方法I是最常见的方法,使用了1024次循环;方法J则根据平台不同做了区分,在ARM平台下,用嵌入汇编仅用128次循环就完成了同样的操作。这里有朋友会说,为什么不用标准的内存拷贝函数呢?这是因为在源数据里可能含有数据为0的字节,这样的话,标准库函数会提前结束而不会完成我们要求的操作。这个例程典型应用于LCD数据的拷贝过程。根据不同的CPU,熟练使用相应的嵌入汇编,可以大大提高程序执行的效率。
& S4 w/ {! {- U& R
5 `- R: H& P0 ~2 G, Z  虽然是必杀技,但是如果轻易使用会付出惨重的代价。这是因为,使用了嵌入汇编,便限制了程序的可移植性,使程序在不同平台移植的过程中,卧虎藏龙,险象环生!同时该招数也与现代软件工程的思想相违背,只有在迫不得已的情况下才可以采用。切记,切记。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-8-14 02:14 , Processed in 0.017368 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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