討論區快速選單
知識庫快速選單
政府補助!學嵌入式+物聯網 網路投保旅行平安險 討論區最近新進100則主題
[ 回上頁 ] [ 討論區發言規則 ]
C: (int*)malloc(M*N*sizeof(int)) 宣告陣列的好處?
更改我的閱讀文章字型大小
作者 : err(err) 人氣指數超過10000點
[ 貼文 27 | 人氣 10053 | 評價 0 | 評價/貼文 0 | 送出評價 14 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2008/7/31 下午 09:44:13
我剛學 C

二維陣列的宣告與元素存取我會用:

int M = 2, int N = 3;
int row = 1, int col = 2;

int A[M][N];
A[row][col] = 9;

為什麼有的書上範例會用:

int M = 2, int N = 3;
int row = 1, int col = 2;

int *A;
A = (int*)malloc(M * N * sizeof(int));
A[row*N+col] = 9;

查過 malloc() 與 sizeof() 的說明, 大概知道 malloc(M * N * sizeof(int)) 表示:
配置 M x N 陣列的 M*N 個元素所需的記憶體大小共 M*N*sizeof(int) bytes,
並傳回起始位址. 若有錯還請指正
1. 不知前頭的 (int*) 是何意?
2. 用 A[row*N+col] 的方式存取陣列元素感到很不直覺.
不知書上所用的整個陣列宣告與元素存取方式有什麼好處?

懇請賜教, 謝謝.
作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
主題發起人err註記此篇回應為最佳解答 2008/8/1 上午 01:10:42
>我剛學 C
>
>二維陣列的宣告與元素存取我會用:
>
>int M = 2, int N = 3;
>int row = 1, int col = 2;
>
>int A[M][N];

這是錯誤的語法. 符合語言標準的編譯器不可能讓你這麼做.

C 語言只支援固定陣列及動態配置陣列. 固定陣列在編譯期就要設定大小的; 動態配置可以在執行期才決定大小.

>A[row][col] = 9;


〔固定陣列〕
  #define MAX_M 10
  #define MAX_N 5
  ...
  int main()
  {
    int A[MAX_M][MAX_N];
    ...
  }


>為什麼有的書上範例會用:
>
>int M = 2, int N = 3;
>int row = 1, int col = 2;
>
>int *A;
>A = (int*)malloc(M * N * sizeof(int));
>A[row*N+col] = 9;

因為如果 M 跟 N 都是變數, 用 malloc() 來動態配置才是唯一可行之道.


>查過 malloc() 與 sizeof() 的說明, 大概知道 malloc(M * N * sizeof(int)) 表示:
>配置 M x N 陣列的 M*N 個元素所需的記憶體大小共 M*N*sizeof(int) bytes,
>並傳回起始位址. 若有錯還請指正

沒錯. 傳回的類型是 void*, 一個沒有標榜類型的泛指標 (generic pointer).

在這個例子, 我建議用 sizeof(*A) 或 sizeof *A 來替代 sizeof(int). 這樣當 A 的類型改成其它類型的指標的時候, sizeof() 那裡不需要改.


>1. 不知前頭的 (int*) 是何意?

那個叫 casting 或 type casting. 中文叫『轉型』. 把 void* 轉型為 int*.

但在 C 語言, 這是不需要的. 在 C 語言, void* 轉換至任何其它類型的指標, 或任何其它類型的指標轉換至 void* 時都不需要 cast. 這是語言標準允許的.

如果編譯器產生無法把 void* 轉換至 int* 的錯誤, 那通常是因為你用 C++ 編譯器來編譯.

如果是用 C++ 編譯器的話, 那意味著你是要寫 C++ 程式. 若是如此, 你應該用 new 而不是 malloc.


>2. 用 A[row*N+col] 的方式存取陣列元素感到很不直覺.

這是方法之一, 一次過配置整片記憶體, 用一維的方式來實施二維的概念.

另一個方法是用雙指標來模擬 2 維陣列的操作:

  int **A;
  A = malloc(N * sizeof *A);
  for (n = 0; n < N; ++n)
  {
    A[n] = malloc(M * sizeof **A);
  }

這樣, A 可以用二維陣列的語法:
  for (n = 0; n < N; ++n)
  {
    for (m = 0; m < M; ++m)
    {
      A[n][m] = ...;
    }
  }

釋放記憶體時, 次序要顛倒:
  for (n = 0; n < N; ++n)
  {
    free(A[n]);
  }
  free(A);


>不知書上所用的整個陣列宣告與元素存取方式有什麼好處?

用 malloc(M * N * sizeof(...)) 比較直接, 配置, 釋放只有一次. 但在使用上不太自然, 因為你必須去寫出計算元素位置的邏輯.

用雙指標在配置, 釋放時手續多一些, 但在使用上會比較自然, 跟固定陣列同樣的語法.

各有各得利弊及使用場地.

作者 : err(err) 人氣指數超過10000點
[ 貼文 27 | 人氣 10053 | 評價 0 | 評價/貼文 0 | 送出評價 14 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2008/8/4 下午 12:48:16
> 在這個例子, 我建議用 sizeof(*A) 或 sizeof *A 來替代 sizeof(int).
> 這樣當 A 的類型改成其它類型的指標的時候, sizeof() 那裡不需要改.

這種方法經您的說明, 我現在用成習慣了.
昨天之前學 linked list, 今天開始學用 linked list 設計 stack
書上程式碼是:

struct stackNode
{
int data;
struct stackNode *next;
};
struct stackNode node, *top = NULL;

void push(int value) {
struct stackNode *p = malloc(sizeof(node));
}

在 linked list 的各個範例中, 書上老是會"額外(看起來屬多餘)"宣告一個變數 node,
而範例程式碼中唯一使用到 node 的地方都只有一列:
malloc(sizeof(node))
PS. 當然這一列不見得只執行一次, 當建立一個新節點時會呼叫函式再執行一次

按照您的方法, 我都改成:

struct stackNode
{
int data;
struct stackNode *next;
};
struct stackNode *top = NULL; // 刪除原變數 node 的宣告

void push(int value) {
struct stackNode *p = malloc(sizeof(p)); // 以 p 替換原 node
}

目前在單向 linked list 範例中執行起來都沒問題
不知道持續這樣使用, 未來會不會遇到有不適用之處?
作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
主題發起人err註記此篇回應為很有道理 2008/8/4 下午 08:29:39
>> 在這個例子, 我建議用 sizeof(*A) 或 sizeof *A 來替代 sizeof(int).
>> 這樣當 A 的類型改成其它類型的指標的時候, sizeof() 那裡不需要改.
>
>這種方法經您的說明, 我現在用成習慣了.

用這個方法時要小心 p 跟 *p 的分別.


>昨天之前學 linked list, 今天開始學用 linked list 設計 stack
>書上程式碼是:

〔...略...〕

>按照您的方法, 我都改成:
>
>struct stackNode
>{
> int data;
> struct stackNode *next;
>};
>struct stackNode *top = NULL; // 刪除原變數 node 的宣告
>
>void push(int value) {
> struct stackNode *p = malloc(sizeof(p)); // 以 p 替換原 node

應該是 sizeof(*p) 才對.

p 是個指標, 所以 sizeof(p) 得到的是指標的大小.

sizeof(*p) 或 sizeof *p 得到的是 p 所指向物件的大小. 由於 p 是個指向 struct stackNode 的指標, 所以 *p 才會等於 struct stackNode, 等於原來的 node.


>}
>
>目前在單向 linked list 範例中執行起來都沒問題
>不知道持續這樣使用, 未來會不會遇到有不適用之處?

你更改後的 codes 會有問題, 這會導致不可預期的結果.

作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2189 | 人氣 89850 | 評價 10120 | 評價/貼文 4.62 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2008/8/5 上午 10:37:45
關於二維陣列, 這裡有一些有參考價值的討論, 也看看吧
http://www.programmer-club.com/pc2020v5/forum/ShowSameTitleN.asp?URL=N&board_pc2020=c&id=34046
作者 : shihyu(Bonjour) 人氣指數超過30000點
[ 貼文 125 | 人氣 34887 | 評價 40 | 評價/貼文 0.32 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2008/8/9 下午 07:47:58
to sflam(Raymond)

這是錯誤的語法. 符合語言標準的編譯器不可能讓你這麼做.

C 語言只支援固定陣列及動態配置陣列. 固定陣列在編譯期就要設定大小的; 動態配置可以在執行期才決定大小.

>A[row][col] = 9;

以上講法不對...

在標準c99 已經有支援 , 所以 A[row][col] = 9; // 裡面是變數是OK的
如果是只支援c89 compiler 是不行沒錯
作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2008/8/11 下午 10:20:38
>這是錯誤的語法. 符合語言標準的編譯器不可能讓你這麼做.
>
>C 語言只支援固定陣列及動態配置陣列. 固定陣列在編譯期就要設定大小的; 動態配置可以在執行期才決定大小.
>
>>A[row][col] = 9;
>
>以上講法不對...
>
>在標準c99 已經有支援 ,

一時沒想到 C99 的 VLA. 您說的是對的, 多謝指正.


>所以 A[row][col] = 9; // 裡面是變數是OK的

應該不是這一句. VLA 應該是指:
  int A[M][N];

作者 : xwlin(XWLin) 人氣指數超過10000點
[ 貼文 147 | 人氣 20298 | 評價 430 | 評價/貼文 2.93 | 送出評價 3 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2008/9/2 下午 10:38:16

>to sflam(Raymond)
>
>這是錯誤的語法. 符合語言標準的編譯器不可能讓你這麼做.
>
>C 語言只支援固定陣列及動態配置陣列. 固定陣列在編譯期就要設定大小的; 動態配置可以在執行期才決定大小.
>
>>;A[row][col] = 9;
>
>以上講法不對...
>
>在標準c99 已經有支援 , 所以 A[row][col] = 9; // 裡面是變數是OK的
>如果是只支援c89 compiler 是不行沒錯

A[row][col] = 9;
這種語法目前有支援了?
我以前一直以為因為是動態配置的二維陣列,所以當你要求取[row][col]時,
編譯器因為不知道每個row有多少個元素,所以它計算不出[row][col]的偏移值。
沒想到現在的編譯器已經能計算出來了。
可能是前面在進行malloc()時就偷偷記下來的吧。

不過,如果是這樣的話,不知可不可以?

int func( int *pDArray, int r, int c )
{
     return pDarray[r][c];
}

我想應該是不行吧。因為編譯器不知道每一個row有多少元素。
作者 : morphingman(morphingman)
[ 貼文 102 | 人氣 448 | 評價 150 | 評價/貼文 1.47 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2008/9/4 下午 09:58:51
我覺得可以 雖然我沒試過
以上例簡寫 f(Array, Row, Col), compiler 幫你傳遞不只是 Array, Col, Row, 假如傳一個東西叫 dope vector (Fortran90 就有了) 便可
dope vector struct = {
  address, // allocated by malloc
  lower_row, // == 0 for C language
  upper_row, // say, U1
  stride_row, // S1 == 4 for 4 bytes integer
  lower_col, // == 0 for C language
  upper_col, // say, U2
  stride_col, // S2== 4*U1
}

then, to access Array[i][j], the address will be Array+i*S2+j*S1

語言就是給人方便用的. 語言的設計不直觀, 就要批判.
作者 : franklin(doggie) C++優秀好手
[ 貼文 156 | 人氣 11 | 評價 750 | 評價/貼文 4.81 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2008/9/5 下午 04:25:26
>我覺得可以 雖然我沒試過
不行

>語言就是給人方便用的. 語言的設計不直觀, 就要批判.
不同的程式語言有不同的需求
程式語言的設計者會在程式的彈性、效率、擴充性與方便性中做出取捨

c的內建型別很單純
裡面只有單純的資料
資料的處理完全根據型別而定
所以可以使用轉型的方式對同樣的資料做不同處理
有需要則使用struct及function做出所需的擴充

回到原題
固定長度陣列的個數也是型別的一部份
在一維陣列時作用不明顯(甚至不影響參數傳遞)
但是在二維(含以上)陣列時會拿來做計算位址的依據(也就是內建前文討論的row*N+col這樣的運算)
VLA雖然是動態的
不過它也只是固定長度陣列的一種變型
它的長度是在執行時確定的而已
所以沒辦法在不指定單列長度的情況下把二維陣列直接當參數傳遞

這樣說起來VLA好像沒什麼用
其實倒也不會
之前我們傳遞二維陣列除了必須傳遞每行長度外
還必須自己做row*N+col這樣的運算
像是
int func( int N, int *pDArray, int r, int c )
{
   return pDArray[r*N+c];
}
但是有VLA後可以這樣寫
int func( int N, int pDArray[][N], int r, int c )
{
   return pDArray[r][c];
}
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2189 | 人氣 89850 | 評價 10120 | 評價/貼文 4.62 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2008/9/6 下午 01:20:33
>這樣說起來VLA好像沒什麼用
>其實倒也不會
>之前我們傳遞二維陣列除了必須傳遞每行長度外
>還必須自己做row*N+col這樣的運算

這是不對的, 你可以配置二維陣列, 然後, 如果是 int 的二維陣列, 傳參數時可以用 int** 的型態去傳.

而配置二維陣列的方法, 這裡已經有詳細的討論.
http://www.programmer-club.com/pc2020v5/forum/ShowSameTitleN.asp?URL=N&board_pc2020=c&id=34046
作者 : franklin(doggie) C++優秀好手
[ 貼文 156 | 人氣 11 | 評價 750 | 評價/貼文 4.81 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2008/9/6 下午 01:50:00

>>;這樣說起來VLA好像沒什麼用
>>;其實倒也不會
>>;之前我們傳遞二維陣列除了必須傳遞每行長度外
>>;還必須自己做row*N+col這樣的運算

>這是不對的, 你可以配置二維陣列, 然後, 如果是 int 的二維陣列, 傳參數時可以用 int** 的型態去傳.
>而配置二維陣列的方法, 這裡已經有詳細的討論.
>http://www.programmer-club.com/pc2020v5/forum/ShowSameTitleN.asp?URL=N&board_pc2020=c&id=34046

這邊我說的不清楚
我用的方法是使用一維陣列模擬出二維陣列的方法
也就是本篇標題所提的方法
而你說的是利用指標陣列產生二維陣列的方法
兩種都有人用
各有優缺點
不過在有VLA能使用可以有第三種方法(也就是我舉的例子)
顯然會比較直觀點
缺點是還沒有什麼compiler支援就是了
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2189 | 人氣 89850 | 評價 10120 | 評價/貼文 4.62 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2008/9/6 下午 02:41:05
關於動態陣列的種種種種, 本論討區已經有非常詳盡的討論, 我建議你先看看它們.

還有, 先小心的查證才立論, 必然比輕率大意的話好的.
作者 : terry7705080991(小恩)
[ 貼文 5 | 人氣 0 | 評價 0 | 評價/貼文 0 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/4/20 下午 02:12:43
我覺得,動態陣列,優點就是,這個陣列不要的時候,你可以把它刪除,這樣就不會佔記憶體了
 板主 : simula
 > C++ - 討論區
 - 最近熱門問答精華集
 - 全部歷史問答精華集
 - C++ - 知識庫
  ■ 全站最新Post列表
  ■ 我的文章收藏
  ■ 我最愛的作者
  ■ 全站文章收藏排行榜
  ■ 全站最愛作者排行榜
  ■  月熱門主題
  ■  季熱門主題
  ■  熱門主題Top 20
  ■  本區Post排行榜
  ■  本區評價排行榜
  ■  全站專家名人榜
  ■  全站Post排行榜
  ■  全站評價排行榜
  ■  全站人氣排行榜
 請輸入關鍵字 
  開始搜尋
 
Top 10
評價排行
C++
1 Raymond 13050 
2 青衫 4760 
3 simula 4690 
4 coco 4030 
5 白老鼠(Gary) 3670 
6 ozzy 2540 
7 Ben 2250 
8 Anderson 1960 
9 windblown 1650 
10 Kenny 1560 
C++
  專家等級 評價  
  一代宗師 10000  
  曠世奇才 5000  
  頂尖高手 3000  
  卓越專家 1500  
  優秀好手 750  
Microsoft Internet Explorer 6.0. Screen 1024x768 pixel. High Color (16 bit).
2000-2019 程式設計俱樂部 http://www.programmer-club.com.tw/
0.21875