您的位置:首页 > 其它

10种使你的C程序更加高效的方法

2011-08-21 22:00 225 查看
http://www.fortystones.com/tips-to-make-c-program-effective/

代码之美不仅在于能够寻求一种合理的解决方案,更在于其简洁、高效与紧凑。代码的设计往往比实际的代码编写要难。因此,每一个程序员在代码编写的过程中,需要在头脑中时常保持一些基本的原则。

这里有10种使你的C程序更加高效的方法:

1.避免不必要的函数调用

考虑下面两个函数调用:

Voidstr_print(char*str) { intI; for(I=0;I<strlen(str);i++){ Printf(“%c”,str[i]); } }

这段代码在循环的过程中不停地调用函数strlen(str),而实际上只需要一次调用即可:

Voidstr_print(char*str) { IntI; Intlen=strlen(str); For(I=0;I<len;i++){ Printf(“%c”,str[i]); } }

2.避免不必要的指针引用:

考虑下面两个函数调用:

Intmultiply(int*num1,int*num2) { *num1=*num2; *num1+=*num2; Return*num1; }

Intmultiply(int*num1,int*num2) { *num1=2**num2; Return*num1; }

第一个例子中有5处指针引用,而第二个例子中只有三处指针引用,你觉得哪一个会更好一些呢?

3.

考虑下面两个结构体:

Struct{ Charc; IntI; Shorts; };

Struct{ Charc; Shorts; IntI; };

假设一个char需要1byte的内存空间,一个short占2byte的内存空间,一个int占4个字节的内存空间。

首先,我们会认为这两个结构体占据相同大小的内存空间,然而,而str_1占用12个字节的第二个结构体只需要8个字节?这怎么可能呢?

内存对齐规则:

①对于结构的各个成员,第一个成员位于偏移为0的位置,以后每个数据成员的偏移量必须是min(#pragmapack()指定的数,这个数据成员的自身长度)的倍数。

②在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragmapack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。

4.

如果你知道某个值永远为正,使用unsignedint而不是int,因为某些处理器处理unsignedint类型的数据比int要快得多。

5.

在逻辑语句中把常量放在运算符的左侧,防止出现拼写错误的情况。

比如:

If(a=3) { a++; }



If(3=a)//CompilationError { a++; }

6.

更多得使用typedef而不是宏,虽然有时宏是不可取代的。Typedef能使程序更加直观。

typedefint*INT_PTR;

INT_PTRa,b;

#defineINT_PTRint*;

INT_PTRa,b;

在这个宏定义中,a是指向int的指针,而b只是int。使用typedef,a、b都是整型指针。

7.

尽量把函数定义为static型除非你希望在其它的文件中调用该函数。Static函数是指只在当前文件中有效的函数。Static型的函数能够有效地限定对函数的访问,这样我们就不需要创建特殊的头文件来存放内部的函数。这样做的好处有:

A).在不同的文件中可以将static函数以相同的名字命名。

B).编译开销减少因为没有外部文件符号处理。

//first_file.c Staticintfoo(inta) { //Whateveryouwanttointhefunction } //second_file.c Intfoo(int); Intmain() { Foo(); //Thisisnotavalidfunctioncallasthefunctionfoocanonlybecalledbyanyotherfunctionwithinfirst_file.cwhereitisdefined. Return0; }

8.

使用记忆,避免递归过程的重复计算。

以Fibonacci数列为例:

常规的递归方法是:

Intfib(intn) { If(n==0||n==1){ Return1; } Else{ Returnfib(n-2)+fib(n-1); } }

从递归树中可以发现,在计算fib(5)时,我们计算fib(3)函数2次,fib(2)函数3次。这是相同函数的重复计算。如果n非常大,fib<n(i)函数增长i<n。解决这一问题的快速方法将是计算函数值1次,存储在一些地方,需要时计算,而非一直重复计算。

intcalc_fib(intn) { intval[n],i; for(i=0;i<=n;i++){ val[i]=-1;//Valueofthefirstn+1termsofthefibonaccitermssetto-1 } val[0]=1;//Valueoffib(0)issetto1 val[1]=1;//Valueoffib(1)issetto1 returnfib(n,val); } intfib(intn,int*value) { if(value[n]!=-1){ returnvalue[n];//Usingmemoization } else{ value[n]=fib(n-2,value)+fib(n-1,value); //Computingthefibonacciterm } returnvalue[n];//Returningthevalue }

9.

避免野指针与悬挂指针。

养成在释放空间之后将指针赋空的习惯。

10.

及时释放内存。

Thebeautyofanycodeliesnotonlyinfindingthesolutiontoagivenproblembutisinitssimplicity,effectiveness,compactnessandefficiency(memory).Designingthecodeisharderthanactuallyimplementingit.Henceeveryprogrammershouldkeep
acoupleofbasicthingsinmindwhileprogramminginC.Hereweintroduceyoutosuch10waysofstandardizingyourCcode.

1.Avoidunwarrantedfunctioncalls

Considerthefollowingtwofunctions:

1


voidstr_print(char*str)


2




3


{


4




5


inti;


6




7


for
(i=0;i<strlen
(str);i++){


8




9


printf("%c",str[i]);


10




11


}


12




13


}


viewsource

print?

1


voidstr_print1(char*str)


2




3


{


4




5


intlen;


6




7


len=strlen
(str);


8




9


for
(i=0;i<len;i++){


10




11


printf("%c",str[i]);


12




13


}


14




15


}


Noticethesimilarityinfunctionofthetwofunctions.Howeverthefirstfunctioncallsthestrlen()multipletimeswhereasthesecondfunctiononlycalls

thefunctionstrlen()asingletime.Thusperformanceofthesecondfunctionisobviouslybetterthanthefirstone.

2.Avoidunnecessarymemoryreferences

Againletstakeacouplemoreexamplestoexplainthis.

1


intmultiply(int*num1,int*num2)


2




3


{


4




5


*num1=*num2;


6




7


*num1+=*num2;


8




9


return
*num1;


10




11


}


1


intmultiply1(int*num1,int*num2)


2




3


{


4




5


*num1=2**num2;


6




7


return
*num1;


8




9


}


Againthesetwofunctionshavesimilarfunctionality.Thedifferenceisthereare5memoryreferencesinthefirstfunction(1forreading*num1,2forreading*num2and2forwritingto*num1)whereasinthesecondfunctionthereisonly2memoryreferences
(oneforreading*num2andoneforwritingto*num1).

Nowwhichonedoyouthinkisbetterofthetwo?

3.Savingmemoryused(conceptofMemoryAlignmentandPadding)

viewsource

print?

1


struct{


2




3


charc;


4




5


inti;


6




7


shorts;


8




9


}str_1;


viewsource

print?

1


struct{


2




3


charc;


4




5


shorts;


6




7


inti;


8




9


}str_2;


Assumethatachartakes1byte,shorttakes2bytesandinttakes4bytesofmemory.Atfirstwewouldthinkthatboththestructuresdefinedabovearethesameandhenceoccupythesameamountofmemory.Howeverwhereasstr_1occupies12bytesthesecond
structuretakesonly8bytes?Howisthatpossible?

Noticeinthefirststructurethat3different4bytesneedtobeassignedtoaccomodatethethreedatatypes(aswehaveintdeclarationbetweencharandshort).Whereasinthesecondstructureinthefirst4bytesbothcharandshortcanbeaccomodated
henceintcanbeaccomodatedinthesecond4bytesboundary(thusatotalof8bytes).

4.Useunsignedintsinsteadofintsifyouknowthevaluewillneverbenegative.Someprocessorscanhandleunsignedintegerarithmeticconsiderablyfasterthansigned(thisisalsogoodpractise,andhelpsmakeforself-documentingcode).

5.Inalogicalconditionalstatementalwayskeeptheconstantitemofthecomparisononthelefthandside

viewsource

print?

1


intx=4;


2




3


if
(x=1){


4




5


x=x+2;


6




7


printf("%d",x);//Outputis3


8




9


}


viewsource

print?

1


intx=4;


2




3


if
(1=x){


4




5


x=x+2;


6




7


printf("%d",x);//Compilationerror


8




9


}


Usingthe“=”assignmentoperatorinsteadofthe“==”equalitycomparisonoperatorisacommontypingerrorwecan’tmakeoutuntilexecution.Puttintheconstanttermonthelefthandsidewillgenerateacompile-timeerror,lettingyoucatchyourerror
easily.

Note:‘=’istheassignmentoperator.b=1willsetthevariablebequaltothevalue1.

‘==’istheequalityoperator.itreturnstrueiftheleftsideisequaltotherightside,andreturnsfalseotherwise.

6.Wheneverpossibleusetypedefinsteadofmacro.Sometimesyoujustcannotavoidmacrosbutusingtypedefisbetter.

viewsource

print?

1


typedefint*INT_PTR;


2




3


INT_PTRa,b;


4




5


#defineINT_PTRint*;


6




7


INT_PTRa,b;


Hereinthemacrodefinitionaisapointertoanintegerwhereasbisdeclaredasonlyaninteger.Usingtypedefbothaandbareintegerpointers.

Inaddition,debuggingwithtypedefismoreintuitivecomparedtomacros.

7.Alwaysdeclareanddefinefunctionsasstaticunlessyouexpectthefunctiontobecalledfromdifferentfiles.

Functionsthatarevisibleonlytootherfunctionsinthesamefileareknownasstaticfunctions.

Itrestrictothersfromaccessingtheinternalfunctionswhichwewanttohidefromoutsideworld.Nowwedon’tneedtocreateprivateheaderfilesforinternalfunctions.Othersdon’tseethefunctionandsotheyhdon’tusethemthereforedon’tcastthose
functiondefinitioninconcrete.

Advantagesofdeclaringafunctionstaticinclude:

a)Twoormorestaticfunctionswiththesamenamecanbeusedindifferentfiles.

b)Compilationoverheadisreducedasthereisnoexternalsymbolprocessing.

Let’sunderstandthisbetterwiththeexamplesbelow:

viewsource

print?

1


/*first_file.c*/


2




3


static
intfoo(inta)


4




5


{


6




7


/*Whateveryouwanttointhefunction*/


8




9


}


10




11


/*second_file.c*/


12




13


intfoo(int)


14




15


intmain()


16




17


{


18




19


foo();//Thisisnotavalidfunctioncallasthefunctionfoocanonlybecalledbyanyotherfunctionwithinfirst_file.cwhereitisdefined.


20




21


return
0;


22




23


}


8.UsememoizationtoavoidrepititiouscalculationinRecursion

ConsidertheFibonacciproblem;

TheFibonacciproblemcanbesolvedbysimplerecursiveapproach:

viewsource

print?

1


intfib(n)


2




3


{


4




5


if
(n==0||n==1){


6




7


return
1;


8




9


}


10




11


else
{


12




13


return
fib(n-2)+fib(n-1);


14




15


}


16




17


}


Note:Hereweareconsideringthefibonacciseriesfrom1.Thustheserieslookslike:1,1,2,3,5,8,…

Noticefromtherecursivetreethatwehavecalculatedthefib(3)function2timesandfib(2)function3times.Thisisrepeatedcalculationforthesamefunction.Ifnisextremelylargethecalculationofthefib(i)functionincreasesfori<n.A
fasterwayofsolvingthisproblemwouldbetocomputethevalueofthefunctiononce,storeitinsomeplaceandretrieveitwheneverneededratherthanrecomputingitalloveragain.Thissimpletechniqueiscalledmemoizationwhichcanbeusedwithrecursion
toenhancethespeedofcomputation.

Thememoizedcodefortheabovefibonaccifunctionwouldlooksomethinglikethis:

viewsource

print?

1


intcalc_fib(intn)


2




3


{


4




5


intval[n],i;


6




7


for
(i=0;i<=n;i++){


8




9


val[i]=-1;//Valueofthefirstn+1termsofthefibonaccitermssetto-1


10




11


}


12




13


val[0]=1;//Valueoffib(0)issetto1


14




15


val[1]=1;//Valueoffib(1)issetto1


16




17


return
fib(n,val);


18




19


}


20




21


intfib(intn,int*value)


22




23


{


24




25


if
(value[n]!=-1){


26




27


return
value[n];//Usingmemoization


28




29


}


30




31


else
{


32




33


value[n]=fib(n-2,value)+fib(n-1,value);//Computingthefibonacciterm


34




35


}


36




37


return
value[n];//Returningthevalue


38




39


}


Herethecalc_fib(n)functioniscalledfromthemain().

9.Avoiddanglingpointersandwildpointers

Apointerwhosepointingobjecthasbeendeletedisknownasadanglingpointer.

Ontheotherhand,wildpointersarethosepointerswhicharenotinitialized.Notethatwildpointersdonotpointanyspecificmemorylocation.

viewsource

print?

1


voiddangling_example()


2




3


{


4




5


int*dp=malloc(sizeof(int));


6




7


/*........*/


8




9


free(dp);//dpisnowadanglingpointer


10




11


dp=NULL;//dpisnolongeradanglingpointer


12




13


}


viewsource

print?

1


voidwild_example()


2




3


{


4




5


int*ptr;//Uninitializedpointer


6




7


printf("%u"\n",ptr);


8




9


printf("%d",*ptr);


10




11


}


Theprogramusuallyshowsweirdbehaviourwhenthesepointersareencountered.

10.Alwaysremembertofreewhatevermemoryyouhaveallocatedinyourprogram.Noticeintheexampleabovehowwefreedthedppointerwhichweallocatedusingthemalloc()functioncall.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: