C ++ 함수에 2D 배열 전달 크기의 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)를 알고 있지만 행 수는 알 수 없으므로 인수로 전달됩니다. 이 경우 2 차원이 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 문은 배열이 참조로 전달되고 있음을 보여줍니다 (변수의 주소를 표시하여)


답변

아무도 이것을 언급하지 않은 것에 놀랐지 만 [] [] 시맨틱을 지원하는 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 “배열 형”데이터 구조와 함께 작동합니다 .


답변

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

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);
}


답변

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원시 배열보다 선호 합니다.