好的 coder 總是有辦法以 1D array 來駕馭高維陣列操作,
但有時使用高維陣列,可讓 code 中的邏輯概念更加清晰。
早期的 C 或目前的 C++ 中,無動態陣列配置的語法,
(在 C99 中才有,例如 gcc 可編譯動態陣列)
因此需要自己實現,底下是我的簡單作法:
設計陣列配置時有 3 點需考量:
1. 配置效率: 最好只呼叫一次 malloc 或 new。
2. 存取效率: 存取速度必須逼近 primary array。
3. 語意: array 的記憶體必須連續分佈。
(所以 vector< vector > 不能算是真正的陣列,
只是個 array container... )
N維動態陣列配置較複雜,先從二維的寫起:
(1) 使用 C 語言:
void** new_arr (int size, int x, int y) //size = 元素大小
{
int sz = sizeof (void*);
void** a = (void **) malloc (sz*x + size*x*y);
if (!a) return 0;
char* p = (char*)a + sz * x;
while (x--) a[x] = p + size*x*y;
return a;
}
void del_arr (void** a)
{
if (a) free (a);
}
void set_arr (void** a, int size, int n...)
{
va_list p;
va_start (p, n);
memcpy (a[0], p, size*n);
}
int main ()
{
typedef struct {float f; char c;} CC;
CC cc[4] = {0.2,'a', 0.4,'b', 0.6,'c', 0.8, 'd'};
int sz;
int **a;
char* **b;
CC **c;
a = (int**) new_arr (sz =
sizeof(int), 2,4);
set_arr ((void**)a, sz, 2*4,
1,2,3,4, //依序輸入資料
7,8,9,0
);
printf ("%d", a[1][2]);
b = (char***) new_arr (sz = sizeof(char*), 2,2);
set_arr ((void**)b, sz, 2*2,
"陣列","2D", //依序輸入資料
"輸入","範例"
);
printf ("/n%s", b[0][1]);
c = (CC**) new_arr (sz = sizeof(CC), 2,2);
set_arr ((void**)c, sz, 2*2,
cc[0], cc[1], //依序輸入資料
cc[2], cc[3]
);
printf ("/n%f %c", c[1][0].f, c[0][0].c);
del_arr ((void**)a);
del_arr ((void**)b);
del_arr ((void**)c);
}
(2) 使用 C++:
template struct Array
{
Array (int x, int y=1): y(y), x(x), p(new T[x*y]) {}
~Array () { for (x*=y; x--; p[x].~T()); delete[] p; }
T* operator[] (int i) { return p+i*y; }
Array& operator< (T o) { p[n=0] = o; return*this; }
Array& operator, (T o) { p[++n] = o; return*this; }
private:
int x, y, n;
T* p;
};
int main ()
{
struct CC {float f; char c;}
cc[4] = {0.2,'a', 0.4,'b', 0.6,'c', 0.8, 'd'};
Array a (2,4);
a < 1,2,3,4, //依序輸入資料
7,8,9,0;
cout << a[1][2];
Array b (2,2);
b < "陣列","模版", //依序輸入資料
"輸入","範例";
cout < c (2,2);
c < cc[0], cc[1], //依序輸入資料
cc[2], cc[3];
cout <
依照相同概念,繼續擴展到 N 維:
C 語言寫法: (此 code 由曾侃的程式修改而來)
#include
void* new_arr (int size, int nDim, int n...)
{
static int d[32] = {0}; //限制最多到 32 維
int i,j, nPtr, nElem, sz = sizeof (void*); //指位器與元素數目
void *a, **b, **c, **t; char *p; //a = 動態陣列起始位址
va_list v;
va_start (v, nDim);
for (i=nDim-1; i>=0; i--) //讀取各個維數
d[i] = va_arg (v, int);
if (0 == nDim) return 0; //禁止配置 0 維陣列
d[nDim-1] = n; //糾正Release版別移掉n
nPtr = d[1]; //計算指位器表大小
nElem = d[0]*d[1]; //與元素所佔之總空間
for (i=2; i
C++ 寫法:
C++ 動態多維陣列目前最有效率的實作是... 暴力法....
也就是用 template 由 1 維一直特製化到 N 維,
此法速度等同 build-in array,亦是 boost::multi_array 的 4~5 倍..
底下另取它徑,實作一個動態陣列的變形,
改以 array (D1, D2, ...) 的形式來存取陣列元素:
template struct Array
{
Array (int d...) //傳入各維的維度
{
dim[1] = dim[N] = 1; //確保()能正確存取
memcpy (dim, &d, N*sizeof(int)); //紀錄各維的維度
for (n=dim[N-1], i=N-2; i>=0; i--)
n *= dim[i], dim[i] = n; //預先計算累加維度
if (0 == n) cout <<"arr size=0"; //配置錯誤
else p = new T[dim[0]]; //配置實體空間
}
T& operator()(int j...)
{
va_start (v,j);
for (n=j*dim[1], i=2; i<=N; ++i)
n += dim[i] * va_arg (v,int); //算出索引偏移量
return p[n];
}
~Array ()
{
for (i=dim[0]; i--; p[i].~T()); //解構陣列內元素
delete[] p;
}
private:
int i,n, dim[N+1]; //dim[.] = 各維的維度
va_list v;
T* p; //陣列實體
};
int main (/* daviddr 080705 */)
{
Array a(2,3,4,5);
for (int i=0; i<2*3*4*5; i++)
a(0,0,0,i) = i;
cout <
為了模擬陣列語法,只好再加些又長又醜陋的碼,
於是現在可以用 arr (D1,D2,..Dn) 與 arr [D1][D2]..[Dn]
兩種方法來存取陣列了~
template struct Array
{
Array (int d...) //傳入各維的維度
{
dim[1] = dim[N] = 1; //確保()能正確存取
memcpy (dim, &d, N*sizeof(int)); //紀錄各維的維度
for (n=dim[N-1], i=N-2; i>=0; i--)
n *= dim[i], dim[i] = n; //預先計算累加維度
if (0 == n) cout <<"arr size=0"; //配置錯誤
else p = new T[dim[0]]; //配置實體空間
}
T& operator()(int j...)
{
va_start (v,j);
for (n=j*dim[1], i=2; i<=N; ++i)
n += dim[i] * va_arg (v,int); //算出索引偏移量
return p[n];
}
~Array ()
{
for (i=dim[0]; i--; p[i].~T()); //解構陣列內元素
delete[] p;
}
template struct Ptr //遞迴定義中繼物件
{
Ptr (T* t, int* d): p(t), d(d) {}
Ptr operator[] (int n) {
return Ptr (p + n*(*d), d+1);
}
operator T*() {return p;}
private: T* p; int* d; //用來傳遞暫時參數
};
template struct Ptr //邊界定義
{
Ptr (T* t, int*): p(t) {}
T& operator[](int n) {return p[n];}
operator T*() {return p;}
private: T* p;
};
Ptr operator[] (int n)
{
return Ptr (p + n*dim[1], dim+2);
}
operator T*() {return p;}
private:
int i,n, dim[N+1]; //dim[.] = 各維的維度
va_list v;
T* p; //陣列實體
};
template struct Array
{
Array (int d): d(d), p(new T[d]) {}
~Array () { while (d--) p[d].~T(); delete[] p; }
T& operator[] (int n) {return p[n];}
T& operator() (int n) {return p[n];}
private: T* p; int d;
} ;
int main (/* daviddr 080705 */)
{
int i;
Array a(2,3,4,5);
for (i=0; i<2*3*4*5; i++) a(0,0,0,i) = i;
cout < b(3);
for (i=0; i<3; i++) b(i) = java[i];
cout <