Programing

C ++ 함수에 2D 배열 전달

lottogame 2020. 3. 20. 08:12
반응형

C ++ 함수에 2D 배열 전달


변수 크기의 2D 배열을 매개 변수로 사용하려는 함수가 있습니다.

지금까지 나는 이것을 가지고있다 :

void myFunction(double** myArray){
     myArray[x][y] = 5;
     etc...
}

그리고 내 코드의 다른 곳에서 배열을 선언했습니다.

double anArray[10][10];

그러나 전화 myFunction(anArray)하면 오류가 발생합니다.

전달할 때 배열을 복사하고 싶지 않습니다. 변경 사항은 myFunction의 상태를 변경해야합니다 anArray. 올바르게 이해하면 2D 배열에 대한 포인터 만 인수로 전달하고 싶습니다. 이 함수는 다른 크기의 배열도 수용해야합니다. 그래서 예를 들면, [10][10]하고 [5][5]. 어떻게해야합니까?


2D 배열을 함수에 전달하는 세 가지 방법이 있습니다.

  1. 매개 변수는 2D 배열입니다

    int array[10][10];
    void passFunc(int a[][10])
    {
        // ...
    }
    passFunc(array);
    
  2. 매개 변수는 포인터를 포함하는 배열입니다

    int *array[10];
    for(int i = 0; i < 10; i++)
        array[i] = new int[10];
    void passFunc(int *a[10]) //Array containing pointers
    {
        // ...
    }
    passFunc(array);
    
  3. 매개 변수는 포인터에 대한 포인터입니다.

    int **array;
    array = new int *[10];
    for(int i = 0; i <10; i++)
        array[i] = new int[10];
    void passFunc(int **a)
    {
        // ...
    }
    passFunc(array);
    

고정 크기

1. 참조로 전달

template <size_t rows, size_t cols>
void process_2d_array_template(int (&array)[rows][cols])
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < rows; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < cols; ++j)
            std::cout << array[i][j] << '\t';
        std::cout << std::endl;
    }
}

C ++에서는 차원 정보를 잃지 않고 참조로 배열을 전달하는 것이 가장 안전합니다. 호출자가 잘못된 차원을 전달하는 것에 대해 걱정할 필요가 없기 때문에 (일치하지 않을 때 컴파일러 플래그). 그러나 동적 (프리 스토어) 배열에서는 불가능합니다. 자동 ( 보통 스택 리빙 ) 배열에서만 작동합니다. 즉, 차원은 컴파일 타임에 알려야합니다.

2. 포인터로 전달

void process_2d_array_pointer(int (*array)[5][10])
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < 5; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < 10; ++j)
            std::cout << (*array)[i][j] << '\t';
        std::cout << std::endl;
    }    
}

이전 방법과 동등한 C는 배열을 포인터로 전달합니다. 이것은 배열의 붕괴 포인터 유형 (3) 을 전달하는 것과 혼동되어서는 안됩니다.이 유형 은 일반적이지만 널리 사용되는 방법이지만이 방법보다 덜 안전하지만 더 유연합니다. (1) 과 같이 배열의 모든 차원이 고정되어 컴파일 타임에 알려진 경우이 방법을 사용하십시오. 함수를 호출 할 때 process_2d_array_pointer(&a)첫 번째 요소의 주소가 아닌 배열의 주소가 decay에 의해 전달되어야합니다 process_2d_array_pointer(a).

가변 크기

이것들은 C에서 상속되었지만 덜 안전합니다. 컴파일러는 확인할 방법이 없으므로 호출자가 필요한 크기를 전달합니다. 이 함수는 발신자가 차원으로 전달한 내용 만 뱅킹합니다. 길이가 다른 배열을 변하지 않고 전달할 수 있으므로 위의 것보다 더 유연합니다.

C에서 함수에 직접 배열을 전달하는 것과 같은 것은 없다는 것을 기억해야한다. [C ++에서는 참조 (1) 로 전달 될 수있다 ] (2) 배열 자체가 아닌 배열에 포인터를 전달합니다. 항상 배열을 그대로 전달하는 것은 포인터의 복사 특성으로 인해 포인터 복사 작업이 용이 한 포인터 복사 작업이 됩니다.

3. 소멸 된 유형으로 포인터를 전달 (값)

// int array[][10] is just fancy notation for the same thing
void process_2d_array(int (*array)[10], size_t rows)
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < rows; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < 10; ++j)
            std::cout << array[i][j] << '\t';
        std::cout << std::endl;
    }
}

int array[][10]허용 되지만 위의 구문은 식별자 array가 10 개의 정수 배열에 대한 단일 포인터 라는 것을 분명히하기 때문에 위의 구문에 대해서는 권장하지 않습니다. 이 구문 2D 배열처럼 보이지만 동일한 포인터입니다. 10 개의 정수 배열 여기서 우리는 단일 행의 요소 수를 알고 있지만 (여기서는 열 크기, 10) 행 수를 알 수 없으므로 인수로 전달됩니다. 이 경우 두 번째 차원이 10이 아닌 배열에 대한 포인터가 전달 될 때 컴파일러가 플래그를 지정할 수 있으므로 안전성이 약간 있습니다. 첫 번째 치수는 변화하는 부분이므로 생략 할 수 있습니다. 첫 번째 차원 만 생략 할 수있는 이유 에 대한 근거여기를 참조하십시오 .

포인터로 포인터로 전달

// int *array[10] is just fancy notation for the same thing
void process_pointer_2_pointer(int **array, size_t rows, size_t cols)
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < rows; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < cols; ++j)
            std::cout << array[i][j] << '\t';
        std::cout << std::endl;
    }
}

또 다른 구문은와 int *array[10]동일합니다 int **array. 이 구문에서는 [10]포인터로 쇠퇴 하여 이므로 무시됩니다 int **array. 아마도 전달 된 배열에 열이 10 개 이상 있어야 행 번호가 필요하다는 것은 호출자에게 신호 일 것입니다. 어쨌든 컴파일러는 길이 / 크기 위반에 플래그를 지정하지 않습니다 (전달 된 유형이 포인터에 대한 포인터인지 확인). 따라서 매개 변수로 행 및 열 수를 모두 필요로합니다.

참고 : (4)는 형식 검사가 거의없고 가장 불편하기 때문에 가장 안전 하지 않은 옵션 입니다. 합법적으로이 함수에 2D 배열을 전달할 수는 없습니다. C-FAQ 비난은 하기의 일반적인 대안 int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);은 같은 잠재적으로 정의되지 않은 동작이 발생할 수 인해 어레이로 평탄화한다. 이 방법으로 배열을 전달하는 올바른 방법은 불편한 부분을 가져옵니다. 즉, 각 요소가 실제 전달 될 배열의 각 행을 가리키는 포인터의 추가 (대리) 배열이 필요합니다. 그런 다음이 대리자는 함수로 전달됩니다 (아래 참조). 더 안전하고 깨끗하며 아마도 더 빠른 위의 방법과 동일한 작업을 수행하기 위해이 모든 것.

위의 기능을 테스트하는 드라이버 프로그램은 다음과 같습니다.

#include <iostream>

// copy above functions here

int main()
{
    int a[5][10] = { { } };
    process_2d_array_template(a);
    process_2d_array_pointer(&a);    // <-- notice the unusual usage of addressof (&) operator on an array
    process_2d_array(a, 5);
    // works since a's first dimension decays into a pointer thereby becoming int (*)[10]

    int *b[5];  // surrogate
    for (size_t i = 0; i < 5; ++i)
    {
        b[i] = a[i];
    }
    // another popular way to define b: here the 2D arrays dims may be non-const, runtime var
    // int **b = new int*[5];
    // for (size_t i = 0; i < 5; ++i) b[i] = new int[10];
    process_pointer_2_pointer(b, 5, 10);
    // process_2d_array(b, 5);
    // doesn't work since b's first dimension decays into a pointer thereby becoming int**
}

shengy의 첫 번째 제안을 수정하면 템플릿을 사용하여 함수를 관리하고 삭제 해야하는 포인터 배열을 저장하는 대신 다차원 배열 변수를 사용할 수 있습니다.

template <size_t size_x, size_t size_y>
void func(double (&arr)[size_x][size_y])
{
    printf("%p\n", &arr);
}

int main()
{
    double a1[10][10];
    double a2[5][5];

    printf("%p\n%p\n\n", &a1, &a2);
    func(a1);
    func(a2);

    return 0;
}

print 문은 배열이 참조로 전달되고 있음을 보여줍니다 (변수의 주소를 표시하여)


다음과 같은 함수 템플릿을 만들 수 있습니다.

template<int R, int C>
void myFunction(double (&myArray)[R][C])
{
    myArray[x][y] = 5;
    etc...
}

그런 다음 R과 C를 통해 차원 크기가 모두 있습니다. 각 배열 크기마다 다른 함수가 만들어 지므로 함수가 크고 다양한 배열 크기로 호출하면 비용이 많이들 수 있습니다. 그래도 다음과 같은 함수를 래퍼로 사용할 수 있습니다.

void myFunction(double * arr, int R, int C)
{
    arr[x * C + y] = 5;
    etc...
}

배열을 1 차원으로 취급하고 산술을 사용하여 인덱스의 오프셋을 알아냅니다. 이 경우 템플릿을 다음과 같이 정의합니다.

template<int C, int R>
void myFunction(double (&myArray)[R][C])
{
    myFunction(*myArray, R, C);
}

아무도 이것을 언급하지 않은 것에 놀랐지 만 [] [] 시맨틱을 지원하는 2D로 템플릿을 작성할 수 있습니다.

template <typename TwoD>
void myFunction(TwoD& myArray){
     myArray[x][y] = 5;
     etc...
}

// call with
double anArray[10][10];
myFunction(anArray);

std::vector<std::vector<T>>코드 재사용을 극대화하기 위해, 또는 사용자 정의 유형 과 같은 2D "배열 형"데이터 구조와 함께 작동합니다 .


anArray[10][10]포인터에 대한 포인터가 아니라 double 유형의 100 값을 저장하는 데 적합한 연속 메모리 덩어리입니다. 컴파일러는 치수를 지정했기 때문에 주소 지정 방법을 알고 있습니다. 이를 배열로 함수에 전달해야합니다. 다음과 같이 초기 치수의 크기를 생략 할 수 있습니다.

void f(double p[][10]) {
}

그러나 이렇게하면 마지막 차원이 10이 아닌 배열을 전달할 수 없습니다.

C ++에서 가장 좋은 솔루션은 사용하는 std::vector<std::vector<double> >것입니다. 거의 효율적이고 훨씬 더 편리합니다.


1 차원 배열은 배열의 첫 번째 요소를 가리키는 포인터 포인터로 감소합니다. 2D 배열은 첫 번째 행을 가리키는 포인터로 붕괴합니다. 따라서 함수 프로토 타입은-

void myFunction(double (*myArray) [10]);

std::vector원시 배열보다 선호 합니다.


이런 식으로 할 수 있습니다 ...

#include<iostream>

using namespace std;

//for changing values in 2D array
void myFunc(double *a,int rows,int cols){
    for(int i=0;i<rows;i++){
        for(int j=0;j<cols;j++){
            *(a+ i*rows + j)+=10.0;
        }
    }
}

//for printing 2D array,similar to myFunc
void printArray(double *a,int rows,int cols){
    cout<<"Printing your array...\n";
    for(int i=0;i<rows;i++){
        for(int j=0;j<cols;j++){
            cout<<*(a+ i*rows + j)<<"  ";
        }
    cout<<"\n";
    }
}

int main(){
    //declare and initialize your array
    double a[2][2]={{1.5 , 2.5},{3.5 , 4.5}};

    //the 1st argument is the address of the first row i.e
    //the first 1D array
    //the 2nd argument is the no of rows of your array
    //the 3rd argument is the no of columns of your array
    myFunc(a[0],2,2);

    //same way as myFunc
    printArray(a[0],2,2);

    return 0;
}

출력은 다음과 같습니다.

11.5  12.5
13.5  14.5

다음은 벡터 행렬 예제로 구성된 벡터입니다.

#include <iostream>
#include <vector>
using namespace std;

typedef vector< vector<int> > Matrix;

void print(Matrix& m)
{
   int M=m.size();
   int N=m[0].size();
   for(int i=0; i<M; i++) {
      for(int j=0; j<N; j++)
         cout << m[i][j] << " ";
      cout << endl;
   }
   cout << endl;
}


int main()
{
    Matrix m = { {1,2,3,4},
                 {5,6,7,8},
                 {9,1,2,3} };
    print(m);

    //To initialize a 3 x 4 matrix with 0:
    Matrix n( 3,vector<int>(4,0));
    print(n);
    return 0;
}

산출:

1 2 3 4
5 6 7 8
9 1 2 3

0 0 0 0
0 0 0 0
0 0 0 0

몇 가지 방법으로 2D 배열을 함수에 전달할 수 있습니다.

  • 단일 포인터를 사용 하여 2D 배열을 타입 캐스트해야합니다.

    #include<bits/stdc++.h>
    using namespace std;
    
    
    void func(int *arr, int m, int n)
    {
        for (int i=0; i<m; i++)
        {
           for (int j=0; j<n; j++)
           {
              cout<<*((arr+i*n) + j)<<" ";
           }
           cout<<endl;
        }
    }
    
    int main()
    {
        int m = 3, n = 3;
        int arr[m][n] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
        func((int *)arr, m, n);
        return 0;
    }
    
  • 이중 포인터 사용 이런 식으로 2d 배열을 타입 캐스트합니다.

    #include<bits/stdc++.h>
    using namespace std;

   void func(int **arr, int row, int col)
   {
      for (int i=0; i<row; i++)
      {
         for(int j=0 ; j<col; j++)
         {
           cout<<arr[i][j]<<" ";
         }
         printf("\n");
      }
   }

  int main()
  {
     int row, colum;
     cin>>row>>colum;
     int** arr = new int*[row];

     for(int i=0; i<row; i++)
     {
        arr[i] = new int[colum];
     }

     for(int i=0; i<row; i++)
     {
         for(int j=0; j<colum; j++)
         {
            cin>>arr[i][j];
         }
     }
     func(arr, row, colum);

     return 0;
   }

다차원 배열을 전달할 때 중요한 것은 다음과 같습니다.

  • First array dimension 지정할 필요가 없습니다.
  • Second(any any further)dimension 지정해야합니다.

1. 두 번째 차원 만 전체적으로 사용할 수있는 경우 (매크로 또는 전역 상수로)

`const int N = 3;

`void print(int arr[][N], int m)
{
int i, j;
for (i = 0; i < m; i++)
  for (j = 0; j < N; j++)
    printf("%d ", arr[i][j]);
}`

int main()
{
int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
print(arr, 3);
return 0;
}`

단일 포인터 사용 :이 방법에서는 함수에 전달할 때 2D 배열을 타입 캐스팅해야합니다.

`void print(int *arr, int m, int n)
{
int i, j;
for (i = 0; i < m; i++)
  for (j = 0; j < n; j++)
    printf("%d ", *((arr+i*n) + j));
 }

`int main()
{
int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int m = 3, n = 3;

// We can also use "print(&arr[0][0], m, n);"
print((int *)arr, m, n);
return 0;
}`

이를 위해 C ++에서 템플릿 기능을 사용할 수 있습니다. 나는 이런 식으로했다 :

template<typename T, size_t col>
T process(T a[][col], size_t row) {
...
}

이 방법의 문제점은 제공하는 모든 col 값에 대해 새 함수 정의가 템플리트를 사용하여 인스턴스화된다는 것입니다. 그래서,

int some_mat[3][3], another_mat[4,5];
process(some_mat, 3);
process(another_mat, 4);

템플릿을 두 번 인스턴스화하여 2 개의 함수 정의 (하나는 col = 3이고 다른 하나는 col = 5)를 생성합니다.


통과 int a[2][3]하려면 void func(int** pp)다음과 같은 보조 단계가 필요합니다.

int a[2][3];
int* p[2] = {a[0],a[1]};
int** pp = p;

func(pp);

첫 번째 [2]는 암시 적으로 지정 될 수 있으므로 다음과 같이 더 단순화 될 수 있습니다.

int a[][3];
int* p[] = {a[0],a[1]};
int** pp = p;

func(pp);

동적 크기의 2 차원 배열을 함수에 전달하려는 경우 일부 포인터를 사용하면 효과가 있습니다.

void func1(int *arr, int n, int m){
    ...
    int i_j_the_element = arr[i * m + j];  // use the idiom of i * m + j for arr[i][j] 
    ...
}

void func2(){
    ...
    int arr[n][m];
    ...
    func1(&(arr[0][0]), n, m);
}

참고 URL : https://stackoverflow.com/questions/8767166/passing-a-2d-array-to-ac-function

반응형