Board logo

主題: [C&C++] [分享]C語言多維陣列傳輸至副程式的方法 [打印本頁]

發表人: 瘋神    時間: 2009-9-27 01:24 AM     主題: [分享]C語言多維陣列傳輸至副程式的方法

最近突然翻到了小弟過去學C語言時的一些筆記,想說就分享出來。

話說到底分模的函式是叫副程式還是副函式啊?

如果有錯,也希望各位大大不吝惜指教,謝謝!

----------------本文開始-----------------

首先,複習一下副函式的標頭

標準格式:回傳值型態 副程式名(傳入值1的型態, 傳入值2的型態, ....)

EX. int test( int, float )

而副函式的主體則是

int test( int a, float b )
{
        程式內容
        Return 回傳值;
}

注意!與標頭不同的地方在於標頭只需要宣告傳入型態,而主體則是要宣告型態與變數名字,才能在副程式裡使用傳進來的東西。

接下來重點來了,要如何傳陣列進入副程式呢?
這邊說明的是利用指標的方式傳入,當然可能有其它方式,但這邊就不多說了

首先要知道陣列本身就是指標的一種,而陣列的第一格就是指標的頭,用範例說明會比較清楚
EX.
        A[1] = 1;
        *(A+1) = 2;
        Printf(“%d”, A[0] );

這樣輸出的答案是2,這樣大概了解陣列等於指標的事情了吧!

所以到底該怎麼傳入副程式呢?一樣用範例來說比較容易
EX.
        #include <stdio.h>
        void test(int*); //標頭採用整數指標的方式傳入值
        int main()
        {
                int arr[3] = {0}, i = 0;
                test( &arr[0] ) // 陣列的第一格即為他的起始標頭
                for( i = 0 ; i < 3 ; i++ )
                        printf(“%d ”, arr[i]);
        }
        void test(int* arr)
        {
                int i;
                for( i = 0; i < 3 ; i++ )
                        arr[i] = 1;
        }

最後印出的結果應該會是
1 1 1

也許有人會問說,這程式是錯的啦!副程式沒有回傳陣列給主程式啊~怎麼可能在主程式印出來會是1 1 1呢?應該是0 0 0才對吧!

聽起來好像很合理,但是別忘記了,指標是個記憶體位置,不論主程式與副程式,他的記憶體位置都是一樣的,所以在副程式裡面修改記憶體位置存的值時,就相當於主程式那邊的值也做了修改,當然這樣就不需要回傳值了。

如果我們要傳的是浮點數的陣列呢?用上面的範例做個修改就OK了
EX.
        #include <stdio.h>
        void test(float*); //標頭採用浮點數指標的方式傳入值
        int main()
        {
                int arr[3] = {0};
                int i = 0;
                test( &arr[0] ) // 陣列的第一格即為他的起始標頭
                for( i = 0 ; i < 3 ; i++ )
                        printf(“%.2f ”, arr[i]);
        }
        void test(float* arr)
        {
                int i;
                for( i = 0; i < 3 ; i++ )
                        arr[i] = 1.5;
        }

所以輸出就會是
1.50 1.50 1.50

這邊就要說一個簡單的概念了,記憶體分配是以一格一格來分的,傳入的資料型態會決定記憶體的使用標準格數為多少,這樣才能正確使用。舉例來說

int test[3] = {0};
test[1] = 2;
會相當於 *(test + 1 ) = 2;
這時候 test 後面的[+1]其實是代表+1套標準位元組,因為這邊是整數的型態,所以1套就代表是4bytes,若是將 int 改成 double

double test[3] = {0};
*(test + 1 ) = 2;
這時候的[+1]代表的就是加上8個位元組,因為精準浮點數在記憶體中是8個位元組為一單位

這時候應該有反應快的會想說那float 跟 int 都是4個位元組那能不能混用呢?
答案是不行的,因為浮點數跟整數的判斷方法不一樣,同樣4個位元組裡用整數去判別跟用浮點數去判別會出現不一樣的結果,所以是不能混用的。

但是不要搞混了,指標依然會是一個整數的記憶體位置,前面的資料型態只是代表該怎麼去使用資料辨別方式與記憶體空間的判讀方法。

最後就來說說那多維陣列要怎麼傳進去呢?一樣,先來看看下面矩陣相加的例子
EX.
#include <stdio.h>
#include <stdlib.h>
void arraysum( float*, float*, float* );  // 矩陣相加副程式

int i = 0, j = 0; // 跑for使用

int main()
{
  float arr1[3][3] = {1}, arr2[3][3] = {1}, ans[3][3] = {0}; // arr1 2 為輸入 ans為輸出答案
  arraysum( &arr1[0][0], &arr2[0][0], &ans[0][0] );
  
  printf("\n答案為:\n");
  for( i = 0; i < 3 ; i++ )
  {
       for( j = 0; j < 3 ; j++ )
            printf("%2.1f ", ans[i][j] );//輸出相加結果
            
       printf("\n");
   }         
   
  system("PAUSE");       
  return 0;
}
void arraysum( float* arr1, float* arr2, float* ans )
{
     for( i = 0; i < 3 ; i++ )
     {
          for( j = 0; j < 3 ; j++ )
               ans[ i*3 + j ] = arr1[ i*3 + j ] + arr2[ i*3 + j]; //兩陣列相加
     }
}

最後的輸出結果應該會是
2 2 2
2 2 2
2 2 2

我覺得最需要說明的應該就只有ans[ i*3 + j ] = arr1[ i*3 + j ] + arr2[ i*3 + j];
這句吧!

這裡就要扯到多維陣列的分配方式了

簡單說,多維陣列是為了人類方便所設計的,實際上他仍是一維的陣列。
以三維方塊陣列(int a[3][3])為例子
a[1][2] = 3 實際上會相當於 a[5] = 3的結果,以指標來看就會是*(a + 1*3 + 2 ) = 3
這樣應該可以了解吧~每一列的開始其實是接著上一列的位置繼續的,所以可以說其實它根本就是一維陣列的另外一種表示法而已,現在再回去看看上面的範例應該就可以看懂了^^

[瘋神 在  2009-10-3 03:59 PM 作了最後編輯]
發表人: leacks    時間: 2009-9-30 07:46 AM

發表程式時記得要關閉"關閉 Discuz! 代碼 | Discuz! 代碼 可用 "
不然沒人看得懂
因為一定會少字
發表人: 瘋神    時間: 2009-10-3 04:00 PM


引用:
leacks寫到:
發表程式時記得要關閉"關閉 Discuz! 代碼 | Discuz! 代碼 可用 "
不然沒人看得懂
因為一定會少字


了解~謝謝大大提醒,我還在想說為什麼我發表會有斜體?
發表人: psycho    時間: 2009-10-6 07:56 AM

void arraysum( float* arr1, float* arr2, float* ans )
{
     for( i = 0; i < 3 ; i++ )
     {
          for( j = 0; j < 3 ; j++ )
               ans[ i*3 + j ] = arr1[ i*3 + j ] + arr2[ i*3 + j]; //兩陣列相加
     }
}
你這個函數 有點危險喔
傳array要記的傳array size
不然會發生好玩的事情
發表人: psycho    時間: 2009-10-6 08:03 AM

用array 做 parameter 的好處是
他會在pass給function時 會做額外的size checking
發表人: leacks    時間: 2009-12-31 08:54 AM

多維的宣告比較機車
ex:傳a[3][3]

void TestFun(int a[][3])
{
        int b[3][3],i,j;
        for(i=0;i<3;i++)
                for(j=0;j<3;j++)
                        b[i][j]=a[i][j];
}

int _tmain(int argc, _TCHAR* argv[])
{
        int a[3][3]={{1,2,3},
                     {4,5,6},
                     {7,8,9}};
        TestFun(a);
system("pause");
        return 0;
}

當然傳個開頭,再副程式時讓他以為是1維的在還原也是可以
只是頭腦要轉@@
如樓主的方法
發表人: Adsmt    時間: 2010-1-27 11:40 AM

還有 array pointers 和 pointers to array

array pointers, 這是比較常用的方法:

char *ar[10];

for( i = 1; i <= 10; i++)
  ar[i-1] = (char *)malloc(sizeof(char)*i);

一個「陣列指標」的蓋念,就是由指標構成的陣列,所以每一個指標都可以指向一個「不等長度」的記憶體區段,這在使用上會比宣告固定的二維陣列靈活得多。

pointers to arrays 就是相反的概念了:
  int (*abc)[10];

  abc = (int (*)[])malloc(sizeof(int) * 5 * 10);

陣列的元素長度固定,但陣列數量不固定。

這兩個的差別,如果以中文來說的話,前者就是「指標的陣列」;後者則為「指向陣列群的指標群」。
舉例來說,前者相當於去訂做箱子,老闆叫你去訂做十個,但大小由你決定,每個箱子可以完全不同。
後者則是你去買箱子,老闆說他只剩下一種箱子,但數量很多,因此他問你需要多少個。

[Adsmt 在  2010-1-27 11:42 AM 作了最後編輯]
發表人: Adsmt    時間: 2010-1-27 11:54 AM

嚴格來說,陣列並不等於指標。

最簡單的例子:

char ar[10], *br;

br = ar;

printf("ar_addr=%p, br_addr=%p\n", ar, br);
printf("ar_addr=%p, br_addr=%p\n", &ar, &br);

這個程式印出很有趣的結果:
ar_addr=0xbfeedd92, br_addr=0xbfeedd92
ar_addr=0xbfeedd92, br_addr=0xbfeedd8c

我們可以看到指標 br 在 0xbfeedd8c 這個記憶體位置儲存了 0xbfeedd92 這個值。
但陣列 ar_addr 就有趣了,他是在0xbfeedd92這個記憶體位置儲存了 0xbfeedd92 這個值。
對系統而言,陣列是一個可用的記憶體區塊,而不是一個指標。如果你看他的 assembly code 就會發現兩者的不同。

不過在實際運用上大致是差不多。
發表人: 瘋神    時間: 2010-2-9 02:36 AM


引用:
Adsmt寫到:
還有 array pointers 和 pointers to array

array pointers, 這是比較常用的方法:

char *ar[10];

for( i = 1; i <= 10; i++)
  ar[i-1] = (char *)malloc(sizeof(char)*i);

一個「陣列指標」的蓋念,就是由指標構成的陣列,所以每一個指標都可以指向一個「不等長度」的記憶體區段,這在使用上會比宣告固定的二維陣列靈活得多。

pointers to arrays 就是相反的概念了:
  int (*abc)[10];

  abc = (int (*)[])malloc(sizeof(int) * 5 * 10);

陣列的元素長度固定,但陣列數量不固定。

這兩個的差別,如果以中文來說的話,前者就是「指標的陣列」;後者則為「指向陣列群的指標群」。
舉例來說,前者相當於去訂做箱子,老闆叫你去訂做十個,但大小由你決定,每個箱子可以完全不同。
後者則是你去買箱子,老闆說他只剩下一種箱子,但數量很多,因此他問你需要多少個。

[Adsmt 在  2010-1-27 11:42 AM 作了最後編輯]


受教了,小弟最近才學到陣列指標與不定長度的概念而已,經大大這樣說明,了解就更多了,感謝大大分享!
發表人: innova    時間: 2010-7-12 04:05 AM

好麻煩~

我現在遇到 需要傳超過 5個參數的 function
或是需要回傳兩個以上變數的 func.
直接改成 struct.
兩邊都 include 同樣一個 .h
struct 定義在這個 .h 裡面
.h 裡面的 struct 隨時可以隨便亂改
記得改完全部重新 rebuild 就是

struct 裡面可不可以放 pointer?
pointer 再指到另一個 struct 的 pointer??

這就不光是啥 三維陣列/四維陣列 能處理的了....




歡迎光臨 TWed2k (http://twed2k.org/) Powered by Discuz! 4.1.0