Abstract
為什麼需要動態建立二維陣列呢?因為sizex和sizey可能run-time才得知,所以無法使用靜態的方式建立陣列,而二維陣列唯一的優點就是可用subscripting的方式,如ia[y][x]的方式存取陣列,但是這在C語言中並不容易,本文就是要介紹如何動態建立二維陣列。
Introduction
再重複一個觀念,C/C++沒有『真正』支援二維陣列,雖然ia[y][x]的語法看似二維陣列,但骨子裡仍是一維陣列,是利用array of array的方式模擬二維陣列,事實上這就是C#的jagged array,所以當我們要動態建立二維陣列時,要先建立第一個一維陣列(sizey),然後在每個陣列的element上在建立第二個一維陣列(sizex),以這種方式模擬出二維陣列。
1
/*
2
(C) OOMusou 2007
http://oomusou.cnblogs.com
3
4
Filename : ArrayDynamicTwoDim_1.c
5
Compiler : Visual C++ 8.0 / ANSI C
6
Description : Demo how to dynamic allocate 2 dim array on heap by malloc()
7
Release : 05/18/2008 1.0
8
*/
9
#include
<
stdio.h
>
10
#include
<
stdlib.h
>
11
12
void
printTwoDimDynamicArray(
int
**
ia,
const
int
sizex,
const
int
sizey) {
13
int
x, y;
14
for
(y
=
0
; y
!=
sizey;
++
y) {
15
for
(x
=
0
; x
!=
sizex;
++
x)
16
printf(
"
%d
"
, ia[y][x]);
17
18
printf(
"
\n
"
);
19
}
20
}
21
22
int
main() {
23
const
int
sizex
=
3
;
24
const
int
sizey
=
2
;
25
int
x, y;
26
int
**
ia
=
(
int
**
)malloc(sizey
*
sizeof
(
void
*
));
27
for
(y
=
0
; y
!=
sizey;
++
y)
28
ia[y]
=
(
int
*
)malloc(sizex
*
sizeof
(
int
*
));
29
30
for
(y
=
0
; y
!=
sizey;
++
y) {
31
for
(x
=
0
; x
!=
sizex;
++
x)
32
ia[y][x]
=
y
+
x;
33
}
34
35
printTwoDimDynamicArray(ia, sizex, sizey);
36
37
for
(y
=
0
; y
!=
sizey;
++
y)
38
free(ia[y]);
39
40
free(ia);
41
}
執行結果
0
1
2
1
2
3
26行
int
**
ia
=
(
int
**
)malloc(sizey
*
sizeof
(
void
*
));
我們首先建立出第一個一維陣列,大小為sizey,這裏我們遇到了int **ia這個pointer to pointer,若你的母語是其他語言,看到pointer應該都會怕,我也不例外,更何況這裡是pointer to pointer,我將來會有個專文討論pointer。這裡為什麼會需要pointer to pointer呢?在(原創) 如何動態建立一維陣列? (C/C++) (C) 時,若要用malloc()動態建立一個一維陣列,會用以下寫法:
int
*
ia
=
(
int
*
)malloc((size_t)sizex
*
sizeof
(
int
));
ia代表指向int ia[sizex]第一個element address的pointer,但現在我們是二維陣列,第一個陣列int ia[sizey]的每個element存的不是int,而是第二個陣列int ia[sizex],也就是說,第一個陣列每個element存的是int *,指向第二的陣列的第一個元素,而第一個陣列本身就是int*,兩個pointer加起來,才變成int **。
至於為什麼用sizeof(void *)呢?理論上該用sizeof(int *),但因為C/C++的pointer皆是4個byte,所以使用sizeof(void *)也可,而且可以泛用於所有型別的陣列。
28行的
ia[y]
=
(
int
*
)malloc(()sizex
*
sizeof
(
int
*
));
是在第一個陣列的每個element,在存放第二個陣列,達成array of array。
12行傳遞陣列到function時
void
printTwoDimDynamicArray(
int
**
ia,
const
int
sizex,
const
int
sizey)
用的是int **ia,很多人學靜態二維陣列時,就懷疑為什麼不用pointer to pointer代表二維陣列呢?這裡總算用到了吧!!
該怎麼釋放這個陣列呢?由於是array of array,須先由內層的第二個陣列開始釋放,最後才能釋放外層的第一個陣列。40行到42行的
for
(
y
=
0
; y
!=
sizey;
++
y)
{
free(ia[y]);
}
釋放了第二層的陣列。
40行的
free(ia);
釋放了第一層的陣列。
這是很標準的寫法,連微軟的KB也是建議用這種寫法
INFO: 動態記憶體配置於二維陣列 ,但這種寫法有幾個缺點,第二個陣列的malloc()在for loop中執行,導致memory fragment,且釋放陣列也不方便。若能不在for loop中使用malloc(),則釋放陣列的問題也能解決。我們來看改進的方式。
1
/*
2
(C) OOMusou 2007
http://oomusou.cnblogs.com
3
4
Filename : ArrayDynamicTwoDim_2.c
5
Compiler : Visual C++ 8.0 / ANSI C
6
Description : Demo how to dynamic allocate 2 dim array on heap by malloc()
7
Release : 05/18/2008 1.0
8
*/
9
#include
<
stdio.h
>
10
#include
<
stdlib.h
>
11
12
void
printTwoDimDynamicArray(
int
**
ia,
const
int
sizex,
const
int
sizey) {
13
int
x, y;
14
for
(y
=
0
; y
!=
sizey;
++
y) {
15
for
(x
=
0
; x
!=
sizex;
++
x)
16
printf(
"
%d
"
, ia[y][x]);
17
18
printf(
"
\n
"
);
19
}
20
}
21
22
int
main() {
23
const
int
sizex
=
3
;
24
const
int
sizey
=
2
;
25
int
x, y;
26
int
**
ia
=
(
int
**
)malloc(sizey
*
sizeof
(
void
*
));
27
int
*
iax
=
(
int
*
)malloc(sizey
*
sizex
*
sizeof
(
int
*
));
28
for
(y
=
0
; y
!=
sizey;
++
y, iax
+=
sizex)
29
ia[y]
=
iax;
30
31
for
(y
=
0
; y
!=
sizey;
++
y) {
32
for
(x
=
0
; x
!=
sizex;
++
x)
33
ia[y][x]
=
y
+
x;
34
}
35
36
printTwoDimDynamicArray(ia, sizex, sizey);
37
38
free(ia[
0
]);
39
free(ia);
40
}
執行結果
0
1
2
1
2
3
關鍵在於27行
int
*
iax
=
(
int
*
)malloc(sizey
*
sizex
*
sizeof
(
int
*
));
我們一次將第二個陣列所需的記憶體一次malloc()了‧
28行到29行
for
(
y
=
0
; y
!=
sizey;
++
y, iax
+=
sizex)
{
ia[y] = iax;
}
關鍵在於 iax+=sizex,iax每次隨著sizex而遞增,如此ia[y]就能取得適當的記憶體位址了。
在釋放記憶體方面,38行和39行的
free(ia[
0
]);
free(ia);
因為只malloc()兩次,所以只要釋放兩次即可。
既然malloc()兩次要還要釋放兩次,有沒有辦法只mallc()一次呢?
1
/*
2
(C) OOMusou 2007
http://oomusou.cnblogs.com
3
4
Filename : ArrayDynamicTwoDim_3.c
5
Compiler : Visual C++ 8.0 / ANSI C
6
Description : Demo how to dynamic allocate 2 dim array on heap by malloc()
7
Release : 05/18/2008 1.0
8
*/
9
#include
<
stdio.h
>
10
#include
<
stdlib.h
>
11
12
void
printTwoDimDynamicArray(
int
**
ia,
const
int
sizex,
const
int
sizey) {
13
int
x, y;
14
for
(y
=
0
; y
!=
sizey;
++
y) {
15
for
(x
=
0
; x
!=
sizex;
++
x)
16
printf(
"
%d
"
, ia[y][x]);
17
18
printf(
"
\n
"
);
19
}
20
}
21
22
int
main() {
23
const
int
sizex
=
3
;
24
const
int
sizey
=
2
;
25
int
x, y;
26
int
**
ia
=
(
int
**
)malloc(sizey
*
sizeof
(
void
*
)
+
sizey
*
sizex
*
sizeof
(
int
*
));
27
int
*
iax
=
(
int
*
)(ia
+
sizex);
28
for
(y
=
0
; y
!=
sizey;
++
y, iax
+=
sizex)
29
ia[y]
=
iax;
30
31
for
(y
=
0
; y
!=
sizey;
++
y) {
32
for
(x
=
0
; x
!=
sizex;
++
x)
33
ia[y][x]
=
y
+
x;
34
}
35
36
printTwoDimDynamicArray(ia, sizex, sizey);
37
38
free(ia);
39
}
執行結果
0
1
2
1
2
3
26行將兩行malloc()濃縮成一行。
int
**
ia
=
(
int
**
)malloc(sizey
*
sizeof
(
void
*
)
+
sizey
*
sizex
*
sizeof
(
int
*
));
28行到29行的
for
(
y
=
0
; y
!=
sizey;
++
y, iax
+=
sizex)
{
ia[y] = iax;
}
由*iax = (int*)(ia + sizex)對iax做初始化,之後每個loop的iax會隨著sizex而遞增,原理和第二個範例一樣。
而釋放陣列方面,只要如40行
free(ia);
釋放一次即可!!
Conclusion
一個很簡單的需求:動態二維陣列,在C語言竟然需要這麼大的功夫才能達成!!假如你還是無法理解,建議參考(原創) 由一維陣列模擬二維陣列(多維陣列) (C/C++) (C),用一維陣列模擬二維陣列就好,唯一就是犧牲了ia[y][x]的語法,而改用*(ia + y * sizex + x)而已,在C++有容易的寫法,請參考(原創) 如何動態建立二維陣列(多維陣列)? (C/C++) (C)‧
See Also
(原創) 由一維陣列模擬二維陣列(多維陣列) (C/C++) (C)
(原創) 如何動態建立一維陣列? (C/C++) (C)
(原創) 如何動態建立二維陣列(多維陣列)? (C/C++) (C)
(原創) 如何動態建立二維陣列(多維陣列)? (C/C++) (C) (C#)
Reference
INFO: 動態記憶體配置於二維陣列
《記憶體的配置》動態陣列