C程式最佳化之路(二) (轉)
本文講述在編寫C程式碼的常用辦法,分為I/O篇,篇,演算法篇,MMX篇。
二.記憶體篇:namespace prefix = o ns = "urn:schemas--com::office" />
在上一篇中我們講述瞭如何最佳化的讀寫,這一篇則主要講述對記憶體操作的最佳化,主要有陣列的定址,指標連結串列等,還有一些實用技巧。
I.最佳化陣列的定址
在編寫程式時,我們常常使用一個一維陣列a[M×N]來模擬二維陣列a[N][M],這個時候訪問a[]一維陣列的時候:我們經常是這樣寫a[j×M+i](對於a[j][i])。這樣寫當然是無可置疑的,但是顯然每個定址語句j×M+i都要進行一次乘法運算。現在再讓我們看看二維數值的定址,說到這裡我們不得不深入到C在申請二維陣列和一維陣列的內部細節上――實際在申請二位陣列和一維陣列,編譯器的處理是不一樣的,申請一個a[N][M]的陣列要比申請一個a[M×N]的陣列佔用的空間大!二維陣列的結構是分為兩部分的:
① 是一個指標陣列,的是每一行的起始地址,這也就是為什麼在a[N][M]中,a[j]是一個指標而不是a[j][0]資料的原因。
② 是真正的M×N的連續資料塊,這解釋了為什麼一個二維陣列可以象一維陣列那樣定址的原因。(即a[j][i]等同於(a[0])[j×M+i])
清楚了這些,我們就可以知道二維陣列要比(模擬該二維陣列的)一維陣列定址高。因為a[j][i]的定址僅僅是訪問指標陣列得到j行的地址,然後再+i,是沒有乘法運算的!
所以,在處理一維陣列的時候,我們常常採用下面的最佳化辦法:(偽碼例子)
int a[M*N];
int *b=a;
for(…){
b[…]=…;
…………
b[…]=…;
b+=M;
}
這個是遍歷訪問陣列的一個最佳化例子,每次b+=M就使得b為下一行的頭指標。當然如果你願意的話,可以自己定義一個陣列指標來儲存每一行的起始地址。然後按照二維陣列的定址辦法來處理一維陣列。不過,在這裡我建議你乾脆就直接申請一個二維陣列比較的好。下面是動態申請和釋放一個二維陣列的C程式碼。
int get_mem2Dint(int ***array2D, int rows, int columns) //h.263
{
int i;
if((*array2D = (int**)calloc(rows, sizeof(int*))) == NULL) no_mem_exit(1);
if(((*array2D)[0] = (int* )calloc(rows*columns,sizeof(int ))) == NULL) no_mem_exit(1);
for(i=1 ; i (*array2D)[i] = (*array2D)[i-1] + columns ; return rows*columns*sizeof(int); } void free_mem2D(byte **array2D) { if (array2D){ if (array2D[0]) free (array2D[0]); else error ("free_mem2D: trying to free unused memory",100); free (array2D); } else{ error ("free_mem2D: trying to free unused memory",100); } } 順便說一下,如果你的陣列定址有一個偏移量的話,不要寫為a[x+offset],而應該為 b=a+offset,然後訪問b[x]。 不過,如果你不是處理對速度有特別要求的程式的話,這樣的最佳化也就不必要了。記住,如果編普通程式的話,可讀性和可移值性是第一位的。 II.從負數開始的陣列 在的時候,你是不是經常要處理邊界問題呢?在處理邊界問題的時候,經常下標是從負數開始的,通常我們的處理是將邊界處理分離出來,單獨用額外的程式碼寫。那麼當你知道如何使用從負數開始的陣列的時候,邊界處理就方便多了。下面是靜態使用一個從-1開始的陣列: int a[M]; int *pa=a+1; 現在如果你使用pa訪問a的時候就是從-1到M-2了,就是這麼簡單。(如果你動態申請a的話,free(a)可不要free(pa)因為pa不是陣列的頭地址) III.我們需要連結串列嗎 相信大家在學習《資料結構》的時候,對連結串列是相當熟悉了,所以我看有人在編寫一些耗時演算法的時候,也採用了連結串列的形式。這樣編寫當然對記憶體的佔用(似乎)少了,可是速度呢?如果你測試:申請並遍歷10000個元素連結串列的時間與遍歷相同元素的陣列的時間,你就會發現時間相差了百倍!(以前測試過一個演算法,用連結串列是1分鐘,用陣列是4秒鐘)。所以這裡我的建議是:在編寫耗時大的程式碼時,儘可能不要採用連結串列! 其實實際上採用連結串列並不能真正節省記憶體,在編寫很多演算法的時候,我們是知道要佔用多少記憶體的(至少也知道個大概),那麼與其用連結串列一點點的消耗記憶體,不如用陣列一步就把記憶體佔用。採用連結串列的形式一定是在元素比較少,或者該部分基本不耗時的情況下。 (我估計連結串列主要慢是慢在它是一步步申請記憶體的,如果能夠象陣列一樣分配一個大記憶體塊的話,應該也不怎麼耗時,這個沒有具體測試過。僅僅是猜想 :P)
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-977213/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- C程式最佳化之路 (轉)C程式
- C程式最佳化之路(三) (轉)C程式
- C++程式碼最佳化方法總結(二) (轉)C++
- C++ 層次程式碼最佳化 (轉)C++
- Delphi程式碼最佳化(二) 整數篇 (轉)
- C++程式碼最佳化方法總結(一) (轉)C++
- C++程式碼最佳化方法總結(四) (轉)C++
- C++程式碼最佳化方法總結(三) (轉)C++
- CSS程式碼重構與最佳化之路CSS
- 我的C學習之路 (轉)
- 程式設計之路 (轉)程式設計
- 最佳化--C程式設計師之終極標靶 (轉)C程式程式設計師
- 關於C++程式碼最佳化的方法總結(轉)C++
- ORACLE SQL效能最佳化系列 (二) (轉)OracleSQL
- C++程式設計思想筆記之二 (轉)C++程式設計筆記
- C#下資料庫程式設計(二) (轉)C#資料庫程式設計
- Prefer C++ (二) (轉)C++
- 程式設計之路-介面美化 (轉)程式設計
- 重走JAVA程式設計之路(二)註解Java程式設計
- 【新炬網路名師大講堂】TUXEDO的配置最佳化之路二UX
- C#速成(之二) (轉)C#
- 程式人生(二) (轉)
- DircetDraw c/c++ 使用指導(二) (轉)C++
- C/C++自學之路C++
- Delphi 程式碼最佳化——字串篇 (轉)字串
- 如何“玩轉”MongoDB?羅輯思維資料庫效能最佳化之路!MongoDB資料庫
- Oracle SQL效能最佳化系列講座之二(轉)OracleSQL
- 程式碼最佳化試驗——短迴圈最佳化(下) (轉)
- 程式碼最佳化試驗——短迴圈最佳化(上) (轉)
- C#聊天程式 (轉)C#
- Delphi程式碼最佳化 完結篇 (轉)
- 最佳化J2ME程式大小 (轉)
- 一個 Shell 程式的效能最佳化(轉)
- React 進階之路(二)React
- 架構之路(二):效能架構
- C#和C++的速度大比拼(二) (轉)C#C++
- c#之路漫漫長C#
- c# 程式設計學習(二)C#程式設計