工程師的家 訂閱主題,使用悄悄話,設定個人資料 註冊是完全免費的! 論壇日曆 找尋其他會員 常見問題解答 搜尋論壇 論壇主頁  
工程師的家 : Powered by vBulletin version 2.2.5 工程師的家 > 程式語言 > C/C++ > C/C++常被提問的議題 > 《陣列》多維動態陣列
  上一篇主題   下一篇主題
作者
主題 發表新主題    回覆該篇主題
coco
版主

註冊日期: Sep 2002
居住地:
共發表: 1681 篇文章


本主題第 1 篇文章

《陣列》多維動態陣列

多維動態陣列在 C 中 大概只能用 malloc,但這在一維時尚
不構成問題,但需要多維陣列時怎麼辦呢?這算是程式論壇
最常被問到的問題之一了。
我把它整理了相關的回覆,都只用二維做說明,更多維的陣列
類推即可。
就從 C 談起吧!

動態產生一個[m][n]陣列 Array 的方法

code:
int i; int **Array; Array= (int **)malloc(m*sizeof(void *)); for (i=0; i<m; i++) Array=(int *)malloc(n*sizeof(int *));

這樣你就有一個 int Array[m][n]; 可以用了
是C 喔!不是 C++
但這不夠好,若你要的是一個較大的mXn陣列,那麼太多的malloc
會使記憶體碎片化(memory fragment)!沒關係,窮則變,變則通!
問題既出在for loop不斷的 memory allocation,就從那兒下手:
code:
int i; int **Array, *pData; Array= (int **)malloc(m*sizeof(int *)); pData= (int *)malloc(m*n*sizeof(int)); for (i=0; i<m; i++, pData+=n) Array[i]=pData;

注意到嗎?這次只用了二次的malloc。
當要release memory也只要free Array[0] 和 Array 就成了!
(注意先free Array[0] 再 free Array)
如果嫌兩個alloc/free還是太多,也可簡單併成一個:
code:
int i; int **Array, *pData; Array= (int **)malloc(m*sizeof(int *)+m*n*sizeof(int)); for (i=0,pData= (int *)(Array+m); i<m; i++, pData+=n) Array[i]=pData;

要 free 時只要 free Array 就行了,帥吧?
如果用的是C++,那可用的方法就更多了!

《幼幼班》嘗試錯誤的階段
?> int Array[][] = new int [10][20];//這樣行ㄇ?
當然不行! int Array[][]不是一個指標,而且只能有
一維為不定大小。

《小班》終於會從1數到100了
?> 那...
?> int *Array[] = new int [10][20];//這樣行ㄇ?
有點想法了!但可惜的是 '*' 在 C++的語法是修飾前面的
識別字,所以 int *Array[]的意思是 "Array是一個 int 指標
的一維陣列!"

如果能使那個 '*' 以獨立指標型態去宣告Array,就會變成
"Array是一個指標,指向一維 int 的陣列",而我們知道指標
本身就可以當做一維的陣列,那麼是不是就成了"Array是一個二維
的int陣列"?
對了!這正是我們要的!問題是怎麼讓'*'成為獨立指標型態,
不會去修飾前面的int識別字?答案是使用括號:
int (*Array)[20] = new int [10][20];//這是正解!

但問題又來了, new 運算子可以使用 new int [m][n],但
int (*Array)[20] 的 [20]卻沒辦法以 [n] 來取代,所以就
沒辦法做到不定大小的宣告了。所以這只能算是小班的答案,
要做到不定大小的動態多維宣告,加上 STL 的運用,是不錯
的想法。

《中班》vector 模板的運用
vector<int> *array=new vector<int>[m];
for (int i=0; i<n; i++) array[i].reserve(n);

嘿嘿...不錯吧!?不定大小的二維陣列,而且每個維度還
可以隨時調整大小喔!
不過還是有缺點ㄟ!!那行 for loop 看起來有點礙眼,不能
拿掉嗎?拿掉的話,基本上Array 還是二維陣列,但第二維
並沒有預留空間放東西你可以用push.back等成員函式來增加
空間和存放data,但不能在還沒有空間時使用像
array[5][3]=3; 這種陣列的存取方式。沒更好的方法了嗎?
vector 不是有預留空間大小的建構子嗎?像一維的宣告:
vector<int> *Array= new vector <int>(n);
//這不是預留了 n 個元素的陣列了嗎?
只可惜,這是一維的宣告,若你嘗試做這樣的宣告:
vector<int> *Array= new vector <int>[m](n);
編譯器會給你無情的嘲諷:陣列不能呼叫帶參數的建構子!
這是很令人失望的!為十麼不行?不是邏輯的問題,或許下
一版本的C++會可以這樣宣告吧!但為今之計只能自力救濟
了。

《大班》使用模板在模版中
仔細觀察下面的宣告:
vector<vector<int> > Array(m, vector<int>(n));

不用懷疑!這是相當於宣告一個動態二維不定大小的 int Array[m][n];
不相信的話你可以在m,n範圍內存取 Array[i][j] 看看。
若你能一眼看出這是在幹嘛,那麼你早已超出大班的程度了!
若你暫時不知所云,也沒關係,你可以安全的使用它帶給你的好處。
只是你也只好留在大班留班查看了 ^_^

解釋這個宣告,其實不難,但首先你要對模版的使用有相當的了解,當然
還要對 vector 模版的建構子不能陌生。這樣子自然很容易可以看得懂它。
了然於胸了嗎?恭喜你,大班可以畢業了 ^_@


註:大班的答案是Duncan在練功房提出的解答,注意到兩個
兩個角括號中間的空白了嗎?(> >)
               ^-------這裡要有空白
不加空白的話會被語法解析為右移運算子。

coco 在 05-27-2003 02:31 PM 編輯

反應文章 | IP: 已經記錄

Old Post 01-02-2003 02:51 PM
coco 離線中 點擊這裡查看 coco 的個人資料 點擊這裡給 coco 發送悄悄話 搜尋更多 coco 的文章 增加 coco 到您的好友名單中 編輯/刪除文章 回覆/引用
James
ROOKIE

註冊日期: Sep 2002
居住地:
共發表: 169 篇文章


本主題第 2 篇文章

Re: 《陣列》多維動態陣列

兩個角括號中間的空白了嗎?(> >)
在《C++標準程式庫》P.88最底下有提到:

code:
typedef set<int,greater<int> > IntSet;

注意,兩個">"符號之間一定要有一個空格。">>"會被編譯器視為一個右移(right-shift)運算子,從而導致語法錯誤。

James 在 02-11-2003 07:35 AM 編輯

反應文章 | IP: 已經記錄

Old Post 02-11-2003 07:11 AM
James 離線中 點擊這裡查看 James 的個人資料 點擊這裡給 James 發送悄悄話 瀏覽 James 的個人首頁 搜尋更多 James 的文章 增加 James 到您的好友名單中 編輯/刪除文章 回覆/引用
coco
版主

註冊日期: Sep 2002
居住地:
共發表: 1681 篇文章


本主題第 3 篇文章

自製了一個可多維的動態陣列類別

這是一個粗糙的陣列類別,可執行時期決定陣列大小,但不能隨便擴充大小。
(當然有必要的話,你可以很容易的加上可擴充大小的功能)
寫這個程式目的純供學習如何在C++中用類別和運算子覆載來模擬多維陣列。
所以一些不相關的但也許很有用的功能都不放進來(例如泛型指標),以求程式
乾淨易於重點閱讀。
真正的應用,STL 的 vector 會有更多的擴充功能和使用彈性。
這個程式只是供學習和理解像 vector 模擬多維陣列,且在建構
時就已保留好所需的陣列空間是如何運作的。

code:
template <class T> class CArray{ int m_sz; T *m_pData; public: //copy constructor:由於 m_pData是new出來的, //default copy constructor 不能使用,所以得要自製 CArray(const CArray<T>& src){*this= src;} //建構子: n 是陣列的大小,d 是陣列內容的起始值 //要模擬成二維,d 參數的引入是重要關鍵(詳見二維應用範例) CArray(int n=0, const T& d= T()){ m_sz=n; //注意二維以上時,T 可能會是CArray 類別,new T[m_sz]會喚起 //CArray()預設建構子。而預設建構子是由本建構式來提供 if (m_sz) m_pData= new T[m_sz]; else m_pData=NULL; for (int i=0;i<n;i++) m_pData[i]=d; //模擬二維以上時,T可能為CArray型態,這時這裡的 //m_pData[i]=d會呼叫 CArray<T>& operator =() } //解構子,就不用說明了吧! ~CArray(){ if (m_pData) delete [] m_pData;} //二維以上模擬時會用到 const CArray<T>& operator =(const CArray<T>& src){ if (m_pData){ delete [] m_pData; m_pData=NULL; } m_sz= src.m_sz; if (m_sz){ m_pData= new T[m_sz]; //三維以上時 T 可能為 CArray型態,new T[m_sz]會喚起 CArray() for (int i=0; i<m_sz; i++) m_pData[i]=src.m_pData[i]; } return *this; } //下標運算子的重載 T& operator[](int i){return m_pData[i];} const T& operator[](int i)const {return m_pDtat[i];} }; //應用範例 void main() { int i,j,k; int m=2,n=3, o=4; // 一維使用範例 CArray<int> Array1D(m);//相當於 int Array1D[m] for (i=0;i <m; i++) Array1D[i]=i; for (i=0;i <m; i++) cout<<Array1D[i]<<endl; //二維使用範例 CArray<CArray<int> > Array2D(m,CArray<int>(n));//相當於 int Array2D[m][n] for (i=0; i<m;i++){ for (int j=0; j<n;j++){ Array2D[i][j]=i*10+j; } } for (i=0; i<m;i++){ for (j=0; j<n;j++){ cout<<Array2D[i][j]<<endl; } } //三維使用範例 CArray<CArray<CArray<int> > > Array3D(m,CArray<CArray<int> >(n,CArray<int>(o)));//相當於 int Array3D[m][n][o] for (i=0; i<m;i++){ for (j=0; j<n;j++){ for (k=0; k<o;k++){ Array3D[i][j][k]=i*100+j*10+k; } } } for (i=0; i<m;i++){ for (j=0; j<n;j++){ for (k=0; k<o;k++){ cout<<Array3D[i][j][k]<<endl; } } } }


良心的提醒:
當你用用像vector或CArray類別物件當引數傳給函式時,最好以
reference來傳,否則當陣列物件很大時會有可怕的陣列複製發生!
例如:
void func1D(CArray<int> &A);//一維的傳遞
void func2D(CArray<CArray<int> > &A);//二維的傳遞
這時和真正的指標一樣,改變了A的內容也會改變呼叫引數
的內容。

coco 在 05-27-2003 02:43 PM 編輯

反應文章 | IP: 已經記錄

Old Post 05-07-2003 09:43 AM
coco 離線中 點擊這裡查看 coco 的個人資料 點擊這裡給 coco 發送悄悄話 搜尋更多 coco 的文章 增加 coco 到您的好友名單中 編輯/刪除文章 回覆/引用
seaweed
初級會員

註冊日期: Sep 2008
居住地:
共發表: 3 篇文章


本主題第 4 篇文章

小弟還是 c++ 新手

想請問版主


int i;
int **Array, *pData;
Array= (int **)malloc(m*sizeof(int *));
pData= (int *)malloc(m*n*sizeof(int));
for (i=0; i<m; i++, pData+=n)
Array[i]=pData;


注意到嗎?這次只用了二次的malloc。
當要release memory也只要free Array[0] 和 Array 就成了!
(注意先free Array[0] 再 free Array)


為什麼不是

free(pData);
free(Array);

而是

free(Array[0]);
free(Array);


???
既然使用了 pData 這個name來作malloc
怎麼不使用pData來free呢?

謝謝!

反應文章 | IP: 已經記錄

Old Post 09-08-2008 03:13 AM
seaweed 離線中 點擊這裡查看 seaweed 的個人資料 點擊這裡給 seaweed 發送悄悄話 搜尋更多 seaweed 的文章 增加 seaweed 到您的好友名單中 編輯/刪除文章 回覆/引用
TerryW
高級會員

註冊日期: Dec 2004
居住地:
共發表: 113 篇文章


本主題第 5 篇文章

quote:
原發表者是 seaweed

為什麼不是

free(pData);
free(Array);

而是

free(Array[0]);
free(Array);


我認為兩者都可以

只是一定要選一個來做free的動作而已

反應文章 | IP: 已經記錄

Old Post 09-11-2008 09:41 AM
TerryW 離線中 點擊這裡查看 TerryW 的個人資料 點擊這裡給 TerryW 發送悄悄話 搜尋更多 TerryW 的文章 增加 TerryW 到您的好友名單中 編輯/刪除文章 回覆/引用
seaweed
初級會員

註冊日期: Sep 2008
居住地:
共發表: 3 篇文章


本主題第 6 篇文章

謝謝T大

我試過

上者是不行的, 會有問題!


quote:
原發表者是 TerryW

我認為兩者都可以

只是一定要選一個來做free的動作而已

反應文章 | IP: 已經記錄

Old Post 09-12-2008 12:38 PM
seaweed 離線中 點擊這裡查看 seaweed 的個人資料 點擊這裡給 seaweed 發送悄悄話 搜尋更多 seaweed 的文章 增加 seaweed 到您的好友名單中 編輯/刪除文章 回覆/引用
TerryW
高級會員

註冊日期: Dec 2004
居住地:
共發表: 113 篇文章


本主題第 7 篇文章

啊啊沒有注意到一件事

quote:
原發表者是 seaweed
int i;
int **Array, *pData;
Array= (int **)malloc(m*sizeof(int *));
pData= (int *)malloc(m*n*sizeof(int));
for (i=0; i<m; i++, pData+=n)
Array[i]=pData;


紅色的部分就是造成錯誤的原因

pData已經不是指向原來的指標了

反應文章 | IP: 已經記錄

Old Post 09-12-2008 01:12 PM
TerryW 離線中 點擊這裡查看 TerryW 的個人資料 點擊這裡給 TerryW 發送悄悄話 搜尋更多 TerryW 的文章 增加 TerryW 到您的好友名單中 編輯/刪除文章 回覆/引用
seaweed
初級會員

註冊日期: Sep 2008
居住地:
共發表: 3 篇文章


本主題第 8 篇文章

喔~

了解了!

感謝T大的解惑!

反應文章 | IP: 已經記錄

Old Post 09-12-2008 10:45 PM
seaweed 離線中 點擊這裡查看 seaweed 的個人資料 點擊這裡給 seaweed 發送悄悄話 搜尋更多 seaweed 的文章 增加 seaweed 到您的好友名單中 編輯/刪除文章 回覆/引用
所有的時間均為 GMT. 現在的時間是 11:05 AM. 發表新主題    回覆該篇主題
  上一篇主題   下一篇主題
預覽列印結果 | 傳送此頁面給朋友 | 訂閱這個主題

選擇另一個討論區:
主題評分:

討論區規則:
not 可以 發表新主題
not 可以 回覆每篇文章
not 可以 上傳附件檔案
not 可以 編輯您自己的文章
HTML 代碼是 關閉
vB 代碼是 開啟
表情符號 開啟
[IMG] 代碼是 關閉
 

< 聯絡我們 - 工程師的家 >

Powered by: vBulletin Version 2.2.5
Copyright ©2000, 2001, Jelsoft Enterprises Limited.
本論壇所有文章、言論均由作者自行負責,不代表本站立場。