2017年11月13日 星期一

C / C++ 函式傳遞二維陣列 範例與解說

C / C++ 函式傳遞二維陣列 範例與解說

一維陣列的傳遞

一維陣列常見的方法是這樣傳遞的

void fun(int* p){...}

int arr[10]={};
fun(arr);

這時候編譯器會自動將 型態::int[10] 轉成 型態::int* 然後成功的傳遞
但是這裡有個但書,只有最高維度可以自動轉換或計算,剩下的皆要手動指定

舉個例子來說明

// 錯誤
int arr[][] = {{0,1},{2,3}};
// 正確
int arr[][2] = {{0,1},{2,3}};

編譯器只會自動補上 最左邊的[] ,只有最左邊可以留空

在函式上二維的傳遞方式也是一樣的邏輯,想傳遞這個二維可以改成這樣

// 寫法1
void fun(int p[][2]){...}
// 寫法2 (等價於第一種,看個人習慣選擇即可)
void fun(int (*p)[2]){...}

這樣就可以正常傳遞了,只是有個缺點二維的長度被限制了。



不定長度的二維傳遞 - 手動轉型

那如果要傳遞不定長的二維就必須使用指標的指標 int** 來傳遞
不過這樣的用法沒辦法直接從int(*)[n] 轉型,必須手動轉型

如果想要自動轉型成 int** 反推一下就要使用 int* 的陣列來轉型

  • int 的陣列是一個陣列裡面放著一堆int
  • int* 的陣列是一個陣列裡面放著一堆int*

就是說二維陣列,可以想像成有一個一維陣列裡面放著一堆一維陣列

// 宣告二維陣列
int arr1[2][2] = {{1,2},{3,4}};

// 取出一維陣列位址
int* a1 = arr1[0];
int* a2 = arr1[1];

// 把一維地址寫進陣列裡
int* p1[2];
p1[0] = a1; 
p1[1] = a2;

現在p1可以自動轉型成 int** 了,可以將它傳入了 int** 的函式內了

會弄得這麼麻煩是因為維度的長度,本身就屬於型態的一部分。陣列長度10跟陣列長度11的差別就好像 int 跟 char 的差別一樣完全是不一樣的東西

而編譯器只會幫你處理最高維度的自動計算而已,所以二維以上不定長度的傳遞在C語言上比較棘手。C++的話有樣板可以自動處理就沒這個困擾了。

範例

/*****************************************************************************
Name : 
Date : 2018/06/13
By   : CharlotteHonG
Final: 2018/06/13
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#define WIDTH 2
#define HEIGHT 3

void fun(int** p) {
    for (int j = 0; j < HEIGHT; ++j) {
        for (int i = 0; i < WIDTH; ++i) {
            printf("%d, ", p[j][i]);
        } printf("\n");
    }
}

int main(int argc, char **argv) {
    int arr[HEIGHT][WIDTH] = {};
    int* p[HEIGHT];

    for (int j = 0; j < HEIGHT; ++j) {
        p[j] = arr[j];
        for (int i = 0; i < WIDTH; ++i) {
            arr[j][i] = 1;
        }
    }

    fun(p);
    return 0;
}




如何傳遞陣列的長寬

這是轉成指標之後的缺點,他將會遺失陣列的長度資訊,常見辦法

  • 讓函式多一個參數傳入
  • 用陣列的第一個數值來當作長度

C++的樣板可以解決這個問題,寫法如下

template<size_t N, size_t N2>
void fun(int (&arr)[N][N2]) {...};

如此一來就可以完整的傳入鎮列了,在函式內 N1 及 N2 就是傳入的二維長度,一維或是三維以上只要調整N的數量對應即可。

沒有留言:

張貼留言