如何写好的程式Word文档下载推荐.docx
《如何写好的程式Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《如何写好的程式Word文档下载推荐.docx(30页珍藏版)》请在冰豆网上搜索。
}
cout<
可讀性佳的程式風格
for(inti=1;
if(i%2==0)sum=sum+7;
elsesum=sum+i;
可讀性佳又精簡的程式風格
i++)sum+=((i%2)?
i:
7);
(3)函式的參數不要太多,參數太多代表函式可能執行太多工作,應考慮將此函式分割成數個較小的函式。
(4)使用遞迴函式化,有助於快速產生正確且可讀性高的程式,但卻必須付出執行速度緩慢的代價。
unsignedgcd(unsigneda,unsignedb)
if(b==0)returna;
//adividesaand0
elsereturngcd(b,a%b);
//recursivestep
unsignedlongfib(unsignedn)
{return(n<
=1?
n:
fib(n-1)+fib(n-2);
(5)盡量保持程式的單純化,即以簡單直接的方法寫程式。
U[k++]=(S[i]<
S[j]?
S[i++]:
S[j++]);
上一行程式太過複雜,若改寫如下,則容易理解
if(S[i]<
S[j]){U[k]=S[i];
i++;
else{U[k]=S[j];
j++;
k++;
*x+=(*xp=(2*k<
(n-m)?
c[k+1]:
d[k--]));
if(2*k<
n-m)*xp=c[k+1];
else*xp=d[k--];
*x+=*xp;
下列程式可用來複製字元陣列:
intlength=strlen(q);
for(inti=0;
=length;
i++)p[i]=q[i];
此程式可改寫如下:
inti;
for(i=0;
q[i]!
=0;
p[i]=0;
可進一步改寫如下:
while(*q!
=0)*p++=*q++;
*p=0;
while((*p++=*q++)!
=0);
while(*p++=*q++);
//對不熟悉的人會覺得難以理解
注意:
*p++相當於*(p++);
所以*p++=*q++;
相當於
*p=*q;
p++;
q++;
(6)使用正確的資料型態,能減少程式錯誤且加快程式執行速度。
(7)使用有意義的函式與變數名稱,有助於程式自我說明。
(8)變數的使用範圍越小越不容易誤用,因此應該避免使用整體參數,區域變數的宣告盡量延後到給初值的地方再宣告。
(9)結構:
儘量使用結構以減少變數的個數。
//For3Dgeometry
typedefstructPoint3Struct{doublex,y,z;
}Point3;
typedefPoint3Vector3;
typedefstructMatrix4Struct{doubleelement[4][4];
}Matrix4;
typedefstructBox3dStruct{Point3min,max;
}Box3;
(10)盡量使用函式或巨集減少程式的行數,函式或巨集命名要有意義。
巨集命名採用全大寫以便區別。
(11)使用const和inline取代#define。
(12)利用自訂資料型態,資料型態的名稱要有意義,以幫助程式自我說明,資料型態的名稱採用首字大寫以便區別。
typedefunsignedIndex;
typedeflongNumber;
typedeffloatForce;
typedeffloatTorque;
typedeffloatSize;
typedeffloatThickness;
typedeffloatTemperature;
typedeffloatPower;
typedeffloatAngle;
(13)使用命名有意義的符號常數或常數變數代替不變的常數,有助於閱讀。
命名採用全大寫以便區別
常見的符號常數
對應之常數變數
#defineD2R0.0174533
#defineR2D57.29578
#definePI3.1415926536
#defineEXP2.718282
#defineGOLDEN1.618034
#defineZERO1.0E-20
#defineBIG1.e30
constfloatD2R=0.0174533
constfloatR2D=57.29578
constfloatPI=3.1415926536
constfloatEXP=2.718282
constfloatGOLDEN=1.618034
constfloatZERO=1.0E-20
constfloatBIG=1.e30
(14)識別字的命名要有意義,但不要以底線或雙底線開頭,以免與C++內定的識別字衝突。
在程式中應避免對不同的目的使用相同的變數名稱,以避免混淆。
(15)使用全域變數容易發生與區域變數發生重複命名的問題,因此命名時應特別規定以便與其他變數區別,或根本避免採用。
(16)藉由列舉型態的宣告,可以用有意義的名稱取代整數常數,從而提高程式的可讀性。
(17)在二元運算子的前後都置空白字元,有助於閱讀;
但單元運算子與運算元間不可以有空白字元。
(18)逗號後面加入空白字元,有助於閱讀。
(19)運算式中加入冗餘括弧,有助於閱讀。
(20)作“==”運算時,將常數置於運算子左邊比置於右邊好。
這樣的好處是萬一誤將“==”寫成“=”,編譯器就會指出此項錯誤。
例如if(x==5)應寫成if(5==x)。
(21)使用最小開放權限原則可以使程式比較容易修改及維護。
(22)如果傳入函式的數值在函式內為定值,則該參數應宣告為const,以確保他不會無意間被修改。
修飾詞const可加強最小開放權限原則。
(23)由於捨入誤差的影響,算數式的運算順序會影響計算的誤差,應仔細考慮下列情況以避免誤差產生:
(1)兩個相近的數相減,會嚴重丟失有效數字。
Ex:
,當
時,y容易產生捨入誤差,此時應改為
。
時,x容易產生捨入誤差,此時應改為
,當x很大時容易產生捨入誤差,此時應改為
(2)分母越小,商的絕對誤差越大,因此運算過程要合理的安排計算順序,以降低誤差的產生,分母出現0或趨近0的情況要完全避免。
10000/0.01/500應改為10000/(0.01×
500)
(3)注意計算步驟的簡化,以減少計算的次數。
(4)避免數字太大,造成overflow。
可改為
_
3.有效率的程式:
3.1簡單而直覺的程式往往也是效率最差的程式,但好處是容易撰寫、測試和除錯;
此外,也可以用來驗證其他寫法之程式的正確性與效率。
因此寫一個簡單而直覺的程式是寫程式的一個不錯的起點。
3.2完成簡單而直覺的程式後,思考是否有更好的資料結構與演算法。
3.3如果可能盡量使用陣列而不要使用鏈結串列,因為使用陣列的演算法通常較有效率。
3.4如果使用到stack或queue,則應進一步思考改用heap是否更好。
3.5如果是靜態的資料即應使用陣列,事先對陣列排序通常有助於發展較簡單而且快速的方法。
3.6加(+)、減(-)、乘法(*)、位元運算(&
、|)需要的時間差不多;
rand()約2倍加/減法的時間;
除法(/)及模數(%)運算約為5倍加/減法的時間;
開根號(sqrt)約10倍加/減法的時間;
簡單三角運算(sin)約20倍加/減法的時間;
反三角運算(asin)約50倍加/減法的時間;
進階三角運算(sinh)約100倍加/減法的時間,記憶體的配置(malloc、free)約200倍加/減法的時間。
所以除法應該考慮改為乘法,模數(%)可考慮用減法取代,避免使用三角函數:
sin(a)用y/r、cos(a)用x/r取代。
記憶體的配置非常浪費時間,應盡量避免。
如果記憶體不是問題,就不需要使用動態記憶體。
範例:
使用減法求餘數
intmod(unsignedm,unsignedn)
{while(m>
=n)m-=n;
returnm;
#defineMOD(m,n){while(m>
3.7對於恆正的變數應宣告unsigned,宣告unsigned可以使編譯器最佳化的編譯此程式,從而加速執行速度。
針對下列程式:
intmiddle(inta,intb)
{return(a+b)/2;
若middle(即回傳值)恆正,應修改為
{returnunsigned(a+b)/2;
3.8迴圈內的程序往往對執行時間有決定性的影響,因此應該仔細撰寫,不需要在迴圈內的步驟,就應該移出廻圈。
求f=1+t0+m0t1+m0m1t2+…+m0m1…mi-1ti+…=
//直覺的程式
f=1+t[0];
mprod=1;
=n;
{
mprod=1;
for(j=0;
j<
=i-1;
j++)mprod*=m[j];
f+=t[i]*mprod;
//較佳之程式
i++){mprod*=m[i-1];
3.9利用矩陣儲存相同的資料,去除資料的重複計算
for(j=0;
r;
j++)
for(i=0;
c;
{
CL[j][i].x=bx+(i+0.5)*px;
CL[j][i].y=by+(j+0.5)*py;
}
較快的程式:
floattempx[c],tempy[r];
i++)tempx[i]=bx+(i+0.5)*px;
j++)tempy[j]=by+(j+0.5)*py;
CL[j][i].x=tempx[i];
CL[j][i].y=tempy[j];
上述程式可以進一步改進:
floattempx[c],tempy;
tempy=by+(j+0.5)*py;
{CL[j][i].x=tempx[i];
CL[j][i].y=tempy;
此程式將重複的步驟合併,不但加快速度也減少記憶體的使用。
3.10迴路裡面避免使用函數
C是堆疊導向的語言,函式的局部變數及參數均使用堆疊做為暫時的儲存所。
當函式被呼叫時,返回位址也被放入堆疊以備程式執行完後可以返回,把這些資料壓入堆疊的過程稱為callingsequence,而將這些資料自堆疊中取出的過程稱為returningsequence,這些過程都會耗用許多時間。
Example:
voidexchangesort(intn,keytypeS[])
for(indexi=1;
=n-1;
for(indexj=i+1;
j++)if(S[j]<
S[i])exchange(S[i],S[j]);
去除函數:
keytypetemp;
j++)
if(S[j]<
S[i]){temp=S[i];
S[i]=S[j];
S[j]=temp:
3.11使用條件運算子取代ifelse結構
k++;
可改為下列較快的程式:
3.12去除不必要的ifelse結構
考慮下列程式
voidf(inta,intb)
boolb1;
if(a==b)b1=true;
elseb1=false;
上述程式實際上可簡化為:
boolb1=a==b;
將上式表為連乘形式
floatx[n],y,L;
for(unsigni=0;
i++)if(i!
=k)L*=(y-x[i])/(x[k]-x[i]);
for(inti=0,L=1;
k;
i++)L*=(y-x[i])/(x[k]-x[i]);
for(i=k+1;
此程式少了i與k的比較,所以速度較快。
求
其中
for(i=1,sum=0;
=max;
for(j=1;
if(i==1&
&
j==1)continue;
elseif(i==1)sum+=A[j];
elseif(j==1)sum+=B[i];
elsesum+=C[i][j];
改進:
(去除不必要的判斷)
sum=0;
for(j=2;
j++)sum+=A[j];
for(i=2;
i++)sum+=B[i];
for(j=2;
j++)sum+=C[i][j];
3.13巢狀的if/else結構,要將為true機率較大判斷式置於前面
二元搜尋法Binarysearch
(1)常見的寫法:
indexlocation(indexlow,indexhigh)
{indexmid;
if(low>
high)return0;
else{
mid=(unsigned)(low+high)/2;
if(x==S[mid])returnmid;
//Findx.
elseif(x<
S[mid])returnlocation(low,mid-1);
//Searchleftsublist
elsereturnlocation(mid+1,high);
//Searchrightsublist.
(2)改進:
indexmid;
//xS
if(x<
elseif(x>
S[mid])returnlocation(mid+1,high);
elsereturnmid;
3.14去除遞迴:
遞迴演算法比起非遞廻程式耗費更多的執行時間與記憶體。
雖然遞迴函式功能很強,但使用上要小心。
採用遞迴方式的常式會比使用迭代的常式執行速度較慢,需要更多的資源負擔。
遞迴呼叫某個函式可能造成堆疊發生溢位。
因為函式的參數和局部變數是存放在堆疊區,而且每次函式呼叫都會把這些變數複製一份,堆疊空間將會耗盡。
如堆疊空間耗盡,將會發生堆疊溢位。
如果發生在已經除錯的遞迴函式,可以嘗試配置更多的堆疊空間給程式,當撰寫遞迴函式時,必須在所有的遞迴呼叫還沒有執行完畢之前,增加條件敘述句使函式強制返回。
假如沒有加入條件敘述句,函式將自我呼叫直到堆疊耗盡。
發展遞迴函式時,這是常見的錯誤。
當發展遞迴式時,使用大量的輸出敘述句,可觀察函式在執行期間的行為,發現錯誤時,就可以略過執行程序。
求費氏數列之遞迴演算法:
fib(n-1)+fib(n-2));
去除遞迴之費氏數列演算法:
unsignedlongfib2(unsignedn)
Indexi;
unsignedlongf[n];
for(f[0]=0,f[1]=1,i=2;
i++)f[i]=f[i-1]+f[i-2];
returnf[n];
3.15使用環狀移動取代一系列的對調
Insertionsort
voidinsertionsort(unsignedn,keytypeS[])
indexi,j;
keytypex,temp;
for(i=2;
x=S[i];
j=i–1;
while(S[j]>
x&
j>
0)
{
temp=S[j+1];
S[j+1]=S[j];
S[j]=temp;
//SwapS[j]andS[j+1]
j--;
}
keytypex;
0)S[j+1]=S[j--];
S[j+1]=x;
3.16使用sentinel減少比較次數
S[0]=-∞//Sentinal
x)S[j+1]=S[j--];
Binarysearch
voidseqsearch1(indexn,constkeytypeS[],keytypex,index&
location)
{//index&
denotelocationisanoutputparameter.
location=0;
while(++location<
=n&
S[location]!
=x);
if(location>
n)location=0;
加入sentinel
voidseqsearch2(indexn,constkeytypeS[],keytypex,index&
S[n+1]=x;
//SetasentinelattheendofS.
while(S[