programing

C 및 C++ 함수에 다차원 배열을 전달하는 방법

nicescript 2022. 8. 16. 22:19
반응형

C 및 C++ 함수에 다차원 배열을 전달하는 방법

#include<stdio.h>
void print(int *arr[], int s1, int s2) {
    int i, j;
    for(i = 0; i<s1; i++)
        for(j = 0; j<s2; j++)
            printf("%d, ", *((arr+i)+j));
}

int main() {
    int a[4][4] = {{0}};
    print(a,4,4);
}

이것은 C에서는 동작하지만 C++에서는 동작하지 않습니다.

에러:

cannot convert `int (*)[4]' to `int**' for argument `1' to 
`void print(int**, int, int)'

C++에서는 왜 동작하지 않는 거죠?어떤 변경이 필요합니까?

1D 어레이를 전달하려면 여기서 다른 답변을 참조하십시오. C의 함수에 어레이를 인수로 전달

급할 경우:

바로 아래로 이동하여 "결론 및 권장사항 요약" 아래에 있는 4개의 인쇄 예를 살펴봅니다.「」의 항.

C 및 C++의 함수 파라미터로서 다차원(예: 2D) 어레이와 이에 대한 포인터를 사용하는 방법

다차원 어레이는 경험이 풍부한 프로그래머나 초보자 모두에게 매우 혼란스럽습니다.그래서 몇 가지 명확한 데모와 따르기 쉬운 가이드라인과 함께 몇 번이고 참조할 수 있는 표준적인 예를 제시하겠습니다(다만, 이 답변 아래의 코멘트 참조).자, 시작합니다.

선행 주의:

  1. NB: C2x(C20 이후)부터 다음과 같은 "원래 원칙" 지침 15가 적용됩니다(출처:Wikipedia: C2x --> 원본 소스: Programming Language C - C2x Charter:

    1. Application Programming Interface(API; 응용 프로그램프로그래밍 인터페이스)는 가능한 한 자체 문서화해야 합니다.특히 함수 선언의 파라미터 순서는 배열의 크기가 배열 앞에 나타나도록 배열해야 합니다.목적은 가변 길이 배열(VLA) 표기법을 사용할 수 있도록 하는 것입니다.이는 인간 독자들에게 코드의 목적을 명확하게 할 뿐만 아니라 정적 분석을 더 쉽게 한다.표준에 추가된 모든 새로운 API는 이를 고려해야 한다.

    따라서 C2x에 준거하고 싶은 경우(지금까지 대부분의 표준 C 함수는 그렇지 않음) 아래의 모든 함수를 다시 배열하여 어레이 크기 인수 또는 어레이 포인터 인수 앞에 배치하십시오.

  2. 이 질문에 대한 답변으로 이 답변을 시작했습니다.배열 포인터를 내 함수에 전달.근데 여기가 더 잘 맞아서 여기다 놔두고 있어요.

  3. 다음 코드는 eRCaGuy_hello_world repo에 있습니다.c / array _ 2d _ practice . cC와 C++ 양쪽에서 컴파일되어 실행됩니다(C11과 C++17에서 테스트됨).소스 코드 파일 상단에 있는 빌드 및 실행 명령을 참조하십시오.gcc 빌드 옵션 사용-Wall -Wextra -Werror안전을 위해서.

  4. 제 답변은 다차원 2D 어레이에 초점을 맞추고 있지만, 3D, 4D, 5D 등 다양한 차원으로 쉽게 확장할 수 있습니다.ND 어레이

  5. 용 i i i i를 쓴다.const배열을 수정하지 않고 인쇄만 하고 있기 때문에 인쇄 기능을 사용할 수 있습니다.하다.const★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

어레이에 관한 주의사항:

  1. 고정 치수 및 미지정 치수:

    어레이는 첫 번째(가장 바깥쪽) 치수를 제외한 모든 치수에 대해 고정(지정) 크기를 지정해야 합니다.이 치수는 옵션으로 지정할 수 없습니다.

    // OK; 1D array with unspecified 1st (and only) dimension
    int array[] = {1, 2, 3};
    // OK; 2D array with unspecified 1st dimensions
    int array[][2] = {{1, 2}, {3, 4}};
    // NOT allowed; 2D array with both dimensions unspecified!:
    // `error: array type has incomplete element type ‘int[]’`
    int array[][] = {{1, 2}, {3, 4}};
    
  2. 어레이 유형의 자연형 붕괴:

    우선, 「어레이 타입」과 「포인트 투 어레이 타입」을 구별합니다.어레이 타입은 어레이, 포인터는 어레이 타입입니다.포인터는 이미 포인터이기 때문에 (AKA가 "조정"하여) 포인터로 감쇠할 수 없습니다.그러나 어레이는 포인터가 되기 위해 ("조정"되는) 경우가 있습니다.

    (1) 어레이 타입의 몇 가지 예를 다음에 나타냅니다.즉, 어레이는 통상적인 「어레이」일 뿐입니다.int array_2d[][2],int* array_2d[],int array_2d[3][2], 두는 ints의 2D 배열, ints의 int*2D로 하다.

    (2) 단, 이것은 어레이 타입에 대한 ptr 또는 "어레이에 대한 포인터"입니다.int (*array_2d)[3][2] 있습니다를 들어 ptrs는 ptr이다.(*)괄호 되어 있어요.★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★따라서 위의 첫 번째 3개의 배열은 인수로 사용되면 ptrs로 감소하지만 마지막 배열은 ptr이기 때문에 감소하지 않습니다.

    원칙은 함수 파라미터로 사용되는 경우 모든 배열 유형(배열 유형에는 해당되지 않음)이 첫 번째 치수의 크기가 명시적으로 지정되었는지 여부에 관계없이 ptr로 축소됩니다.그래서, 하지만...int arr[]) 및 ('t')int * arr는이int)이 됩니다.그 중 하나가 포함된 함수 정의는 어레이의 첫 번째 차원(이 1D 어레이 케이스에서는 유일한 차원)을 포인터로 자연스럽게 붕괴시켜 타입(type)이 됩니다.int * arr

    // accepts `int *` as `array` parameter
    void my_func(int * array, size_t len) {}
    // also accepts `int *` as `array` parameter, since the `int []` type 
    // (array of ints) naturally decays down to type `int *` (ptr to int`).
    void my_func(int array[], size_t len) {}
    

    더 나아가 배열의 첫 번째 치수로 크기를 지정하는 것은 이 영향과는 관계가 없습니다.즉, 컴파일러에는 전혀 차이가 없고 의미가 없습니다.특정 함수는 적어도 이 크기 이상의 배열을 기대하는 프로그래머에 대한 시각적 지표 또는 "자기 문서"의 유형으로 기능할 뿐입니다.여기서 답변(배열을 C의 함수에 대한 인수로서 전달)을 참조해 주세요.또, 이 기능을 자기 문서화에 사용하는 것을 추천하는 MISRA-C 표준을 인용합니다.

    따라서 위의 기능과 모두 동일합니다.

    // same as above: these ALL accept `int *` as the 1st parameter, and the 
    // specified size here in square brackets [] has no effect on the compiler.
    void my_func(int array[1], size_t len) {}
    void my_func(int array[10], size_t len) {}
    void my_func(int array[100], size_t len) {}
    

    이 정도면 충분합니다.

    int array[10];
    my_func(array); // ok; `array` naturally decays down to type `int *`
    

    그리고 이것도 괜찮다.

    int array[10];
    int * array_p = array;
    my_func(array_p); // ok; is already of type `int *`
    

    단, 어레이 포인터의 경우 실제 어레이 유형과 크기가 중요하며 어레이에서 ptr로 자연형 감쇠는 발생하지 않습니다.이는 어레이가 이미 지정된 유형 및 크기의 어레이에 대한 ptr이기 때문입니다.위의 답변을 읽어주세요.

    예: 다음 함수에는 ptr 유형의 입력 파라미터와 10 크기의 1D 배열이 필요합니다.이미 ptr이기 때문에 ptr의 자연형 붕괴는 발생하지 않습니다!이 파라미터는 어레이에 대한 ptr이므로 어레이주소도&다음과 같이 함수를 호출할 때 문자를 입력합니다.콜중 동작하는 것은 뿐이라는 점에 해 주십시오.my_func(&array2); ★★★★★★★★★★★★★★★★★」my_func(array2_p2);다만, 이러한 모든 콜을 표시해, 다양한 어레이 타입, 포인터로 어떻게, 언제, 어떤 타입으로 붕괴하는지를 설명하고, 설명할 수 있도록 했습니다.

    // 0. Define a function
    
    /// `array` is a "ptr to an array of 10 ints". 
    void my_func(int (*array)[10]) {}
    
    // 1. Create arrays
    
    int array1[5];
    int *array1_p = array1; // array1_p is of type `int *` (ptr to int)
    int (*array1_p2)[5] = &array1; // array1_p2 is of type `int (*)[5]` (ptr
                                   // to array of 5 ints)
    
    int array2[10];
    int *array2_p = array2; // array2_p is of type `int *` (ptr to int)
    int (*array2_p2)[10] = &array2; // array2_p2 is of type `int (*)[10]` (ptr
                                    // to array of 10 ints)
    
    // 2. Make some calls 
    
    // 2.1. calling with `int array1[5]`
    
    // FAILS! Expected `int (*)[10]` (ptr to array of 10 ints) but argument is
    // of type `int *` (ptr to int); due to **natural type decay** from
    // `int[5]` (array of 5 ints) to `int *` (ptr to int)
    my_func(array1);
    
    // FAILS! Expected `int (*)[10]` (ptr to array of 10 ints) but argument is
    // of type `int *` (ptr to int); due to dereferencing to `int[5]` (array
    // of 5 ints), followed by **natural type decay** from `int[5]`
    // (array of 5 ints) to `int *` (ptr to int)
    my_func(*array1_p2);
    
    // FAILS! Expected `int (*)[10]` (ptr to array of 10 ints) but argument is
    // of type `int *` (ptr to int)
    my_func(array1_p);
    
    // FAILS! Expected `int (*)[10]` (ptr to array of 10 ints) but argument is
    // of type `int`; due to **natural type decay** from `int[5]` (array of
    // 5 ints) to `int *` (ptr to int), in conjunction with dereferencing
    // from that to `int`
    my_func(*array1);
    
    // FAILS! Expected `int (*)[10]` (ptr to array of 10 ints) but argument is
    // of type `int`
    my_func(*array1_p);
    
    // FAILS! Expected `int (*)[10]` (ptr to array of 10 ints) but argument is
    // of type `int (*)[5]` (ptr to array of 5 ints)
    my_func(&array1);
    
    // FAILS! Expected `int (*)[10]` (ptr to array of 10 ints) but argument is
    // of type `int (*)[5]` (ptr to array of 5 ints)
    my_func(array1_p2);
    
    // FAILS! Expected `int (*)[10]` (ptr to array of 10 ints) but argument is
    // of type `int (**)[5]` (ptr to "ptr to array of 5 ints")
    my_func(&array1_p2);
    
    // 2.2. calling with `int array2[10]`
    
    // FAILS! Expected `int (*)[10]` (ptr to array of 10 ints) but argument is
    // of type `int *` (ptr to int); due to **natural type decay** from
    // `int[10]` (array of 10 ints) to `int *` (ptr to int)
    my_func(array2);
    
    // FAILS! Expected `int (*)[10]` (ptr to array of 10 ints) but argument is
    // of type `int *` (ptr to int); due to dereferencing to `int[10]` (array
    // of 10 ints), followed by **natural type decay** from `int[10]`
    // (array of 10 ints) to `int *` (ptr to int)
    my_func(*array2_p2);
    
    // FAILS! Expected `int (*)[10]` (ptr to array of 10 ints) but argument is
    // of type `int *` (ptr to int)
    my_func(array2_p);
    
    // FAILS! Expected `int (*)[10]` (ptr to array of 10 ints) but argument is
    // of type `int`; due to **natural type decay** from `int[10]` (array of
    // 10 ints) to `int *` (ptr to int), in conjunction with dereferencing
    // from that to `int`
    my_func(*array2);
    
    // FAILS! Expected `int (*)[10]` (ptr to array of 10 ints) but argument is
    // of type `int`
    my_func(*array2_p);
    
    // <===============
    // <=== WORKS! ====
    // <===============
    // Expected and received `int (*)[10]` (ptr to array of 10 ints)
    my_func(&array2);
    
    // <===============
    // <=== WORKS! ====
    // <===============
    // Expected and received `int (*)[10]` (ptr to array of 10 ints)
    my_func(array2_p2);
    
    // FAILS! Expected `int (*)[10]` (ptr to array of 10 ints) but argument is
    // of type `int (**)[10]` (ptr to "ptr to array of 10 ints")
    my_func(&array2_p2);
    
  3. 포인터에 대한 빠른 알림:

    은 「」에 주의해 .int *[2] ★★★★★★★★★★★★★★★★★」int (*)[2]같은 타입이 아닙니다!★★★★★★★★★★★★★★★★★★★!int *[2]'2의 2로 이루어진 입니다.int *int로), s(int'에서 int로), int에서 int로int (*)[2]는 " 2" 입니다.ints". 이 두 가지는 매우 다른 것입니다.

    또, ptr는 어레이와 같이 인덱스 할 수 있기 때문에, 어레이가 ptr, 즉 FALSE라고 하는 매우 빈번한 혼란이 발생합니다.어레이는 ptr이 아닙니다!단, 다음 개념은 아래 코드를 이해하는 데 중요합니다.int array_2d[][2]【2D】 " "array_2d은 「」입니다.int [][2]x 2 즉1) 및2)을 가지는 . (n x 2 (2D) 배 ) n 、 2D 배 ( 1 ) 、 2D ( 2D ) 。파라미터로 이 int [][2]타이핑은 자연스럽게 타이핑하다int (*)[2](ptr to (1D) 배열, 2ints).그렇다면 이 부패형이 ptr이라면 어떻게 배열일까요?ptr은 배열처럼 인덱싱할 수 있으므로 다음과 같은 방법으로 인덱싱할 수 있습니다.array_2d[row][col]은 ptr, ptr은 row, 는 ", " " 입니다[2](2ints), (2ints), (2ints)로할 수 있는 부분.col 배열 배열이므로 umn을 선택합니다., 각 에 2개의되어 있기 2개의 를 붙이면 2개의 int가 됩니다.row ''를 붙여야 column. 따라서 ptrs와 어레이 간의 혼동은 어레이ptrs 아니더라도 모든 ptrs 어레이와 같이 인덱스가 가능하기 때문에 인수로 사용하면 모든 어레이의 번째 차원(어레이에 대한 ptrs가 아님)ptr소멸됩니다.

따라서 위의 개념을 염두에 둔다면 다음과 같은 것이 더 타당할 것입니다.각 함수 정의에 대해 어레이 유형이 무엇인지, 어레이 유형이 자연스럽게 붕괴되는지 여부 및 내용에 주의하십시오.다시 말해 함수 파라미터로 사용되면 모든 비점 배열 유형은 배열의 첫 번째 치수를 ptr로 축소합니다. ptr은 배열과 마찬가지로 인덱스가 가능합니다.

다차원 어레이를 통과하기 위한 결론 및 권장 사항 요약:

다음은 다차원 어레이를 매개 변수로 전달하기 위한 4가지 사용 사례와 기술 및 각각의 사용 시기에 대한 권장 사항입니다.각 기법의 기능 프로토타입과 정의를 통해 각 기법이 제공하는 다양한 단점, 복잡성 및 이점을 확인할 수 있습니다.

다음과 같은 2D 어레이가 있다고 가정합니다.

int arr[][2] =
{
    {1, 2},
    {5, 6},
    {7, 8},
};

...및 다음의 매크로 정의:

// Get the number of elements in any C array
// - from my repo here:
//   https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world/blob/master/c/utilities.h#L42
#define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))

/// Definitions: `rows` = "rows"; `cols` = "columns"

/// Get number of rows in a 2D array
#define NUM_ROWS(array_2d) ARRAY_LEN(array_2d)

/// Get number of columns in a 2D array
#define NUM_COLS(array_2d) ARRAY_LEN(array_2d[0])
  1. 고정 크기 다차원 배열: 2D 배열이 항상 동일한 크기(고정 행 및 열 포함) 경우(아래 예에서는 3개 행과 2개 열) 다음과 같이 하십시오.
    // 1. Function definition
    /// \brief      Print a 2D array which has a FIXED number of rows and
    ///             FIXED number of columns.
    /// \param[in]  array_2d    a 2D array; is of type `int (*)[3][2]` (ptr to
    ///                         3 x 2 (2D) array of ints); since it is already
    ///                         explicitly a ptr, it does NOT naturally decay to
    ///                         any other type of ptr
    /// \return     None
    void print_array2(const int (*array_2d)[3][2])
    {
        printf("print_array2:\n");
        for (size_t row = 0; row < NUM_ROWS(*array_2d); row++)
        {
            for (size_t col = 0; col < NUM_COLS(*array_2d); col++)
            {
                printf("array_2d[%zu][%zu]=%i ", row, col, (*array_2d)[row][col]);
            }
            printf("\n");
        }
        printf("\n");
    }
    
    // 2. Basic usage
    // NB: `&` is REQUIRED! See my answer for why: https://stackoverflow.com/a/51527502/4561887
    print_array2(&arr);
    
    // 3. Usage via a pointer
    // `int (*array_2d)[3][2]` is an explicit ptr to a 3x2 array of `int`. This
    // pointer to an array does NOT naturally decay to a simpler type.
    int (*p2)[3][2] = &arr; // must use `&` and MUST USE THESE PARENTHESIS!
    print_array2(p2);
    
  2. 2D 배열에 가변가 있지만 고정 열 수(이 경우 2개)가 있는 경우 다음을 수행합니다.
    // 1. Function definition
    /// \brief      Print a 2D array which has a VARIABLE number of rows but
    ///             FIXED number of columns.
    /// \param[in]  array_2d    a 2D array; is of type `int [][2]` (n x 2 (2D) array
    ///                         of ints), which naturally decays to type
    ///                         `int (*)[2]` (ptr to (1D) array of 2 ints)
    /// \param[in]  num_rows    The number of rows in the array
    /// \return     None
    void print_array3(const int array_2d[][2], size_t num_rows)
    {
        printf("print_array3:\n");
    
        // Technique 1: use `array_2d` directly.
        printf("--- Technique 1: ---\n");
        for (size_t row = 0; row < num_rows; row++)
        {
            for (size_t col = 0; col < NUM_COLS(array_2d); col++)
            {
                printf("array_2d[%zu][%zu]=%i ", row, col, array_2d[row][col]);
            }
            printf("\n");
        }
    
        // Technique 2: cast the `array_2d` decayed ptr to a ptr to a sized array of
        // the correct size, then use that ptr to the properly-sized array
        // directly! NB: after obtaining this ptr via the cast below, this
        // technique is **exactly identical** to (I copy/pasted it from, then
        // renamed the variable) the implementation inside `print_array2()` above!
        printf("--- Technique 2: ---\n");
        int (*array_2d_ptr)[num_rows][NUM_COLS(array_2d)] =
            (int (*)[num_rows][NUM_COLS(array_2d)])array_2d;
        for (size_t row = 0; row < NUM_ROWS(*array_2d_ptr); row++)
        {
            for (size_t col = 0; col < NUM_COLS(*array_2d_ptr); col++)
            {
                printf("array_2d_ptr[%zu][%zu]=%i ", row, col, (*array_2d_ptr)[row][col]);
            }
            printf("\n");
        }
    
        printf("\n");
    }
    
    // 2. Basic usage
    print_array3(arr, NUM_ROWS(arr));
    
    // 3. Usage via a pointer
    // `int array_2d[][2]` (n x 2 (2D) array of ints) naturally decays to 
    // `int (*)[2]` (ptr to (1D) array of 2 ints)
    int (*p3)[2] = arr; // MUST USE THESE PARENTHESIS!
    print_array3(p3, NUM_ROWS(arr));
    
  3. 2D 어레이에 가변 수와 가변수가 있는 경우 다음과 같이 하십시오(이 접근 방식이 가장 다용도적이므로 일반적으로 다차원 어레이에 대해 선호되는 접근 방식입니다).
    // 1. Function definition
    /// \brief      Print a 2D array which has a VARIABLE number of rows and
    ///             VARIABLE number of columns.
    /// \param[in]  array_2d    a 2D array; is of type `int *` (ptr to int); even
    ///                         though a 1D array of type `int []` (array of ints)
    ///                         naturally decays to this type, don't think about it
    ///                         that way; rather, think of it as a ptr to the first
    ///                         `int` in a contiguous block of memory containing a
    ///                         multidimensional array, and we will manually index
    ///                         into it as required and according to its dimensions
    /// \param[in]  num_rows    The number of rows in the array
    /// \param[in]  num_cols    The number of columns in the array
    /// \return     None
    void print_array4(const int *array_2d, size_t num_rows, size_t num_cols)
    {
        printf("print_array4:\n");
    
        // Technique 1: use `array_2d` directly, manually indexing into this
        // contiguous block of memory holding the 2D array data.
        printf("--- Technique 1: ---\n");
        for (size_t row = 0; row < num_rows; row++)
        {
            const int *row_start = &array_2d[row*num_cols];
    
            for (size_t col = 0; col < num_cols; col++)
            {
                // NB: THIS PART IS VERY DIFFERENT FROM THE OTHERS! Notice `row_start[col]`.
                printf("array_2d[%zu][%zu]=%i ", row, col, row_start[col]);
            }
            printf("\n");
        }
    
        // Technique 2: cast the `array_2d` decayed ptr to a ptr to a sized array of
        // the correct size, then use that ptr to the properly-sized array
        // directly! NB: after obtaining this ptr via the cast below, this
        // technique is **exactly identical** to (I copy/pasted it from, then
        // renamed the variable) the implementation inside `print_array2()` above!
        printf("--- Technique 2: ---\n");
        int (*array_2d_ptr)[num_rows][num_cols] =
            (int (*)[num_rows][num_cols])array_2d;
        for (size_t row = 0; row < NUM_ROWS(*array_2d_ptr); row++)
        {
            for (size_t col = 0; col < NUM_COLS(*array_2d_ptr); col++)
            {
                printf("array_2d_ptr[%zu][%zu]=%i ", row, col, (*array_2d_ptr)[row][col]);
            }
            printf("\n");
        }
    
        printf("\n");
    }
    
    // 2. Basic usage
    print_array4((int *)arr, NUM_ROWS(arr), NUM_COLS(arr));
    // OR: alternative call technique:
    print_array4(&arr[0][0], NUM_ROWS(arr), NUM_COLS(arr));
    
    // 3. Usage via a pointer
    // The easiest one by far!
    int *p4_1 = (int*)arr;
    // OR
    int *p4_2 = &arr[0][0];
    print_array4(p4_1, NUM_ROWS(arr), NUM_COLS(arr));
    print_array4(p4_2, NUM_ROWS(arr), NUM_COLS(arr));
    

그러나 다음과 같은 "2D" 배열이 있는 경우에는 다른 작업을 수행해야 합니다.

// Each row is an array of `int`s.
int row1[] = {1, 2};
int row2[] = {5, 6};
int row3[] = {7, 8};
// This is an array of `int *`, or "pointer to int". The blob of all rows
// together does NOT have to be in contiguous memory. This is very different
// from the `arr` array above, which contains all data in contiguous memory.
int* all_rows[] = {row1, row2, row3}; // "2D" array
  1. 2D 어레이가 실제로 다른 어레이에 대한 다수의 ptrs로 구성되어 있는 경우(위 그림 참조) 다음과 같이 하십시오.
    // 1. Function definition
    /// \brief      Print a 2D-like array, where the array passed in is an array of
    ///             ptrs (int *) to other sub-arrays. Each index into the outer
    ///             array is the row, then each index into a sub-array in a given
    ///             row is the column. This handles a VARIABLE number of rows and
    ///             VARIABLE number of columns.
    /// \details    `array_2d` here is different from all of the cases above. It is
    ///             NOT a contiguous 2D array of `int`s; rather, it is an array of
    ///             pointers to ints, where each pointer in the array can be
    ///             thought of as a sub-array. Therefore, the length of the outer
    ///             array is the number of rows, and the length of each sub-array,
    ///             or inner array, is the number of columns. Each sub-array
    ///             (a single row of `int`s) DOES have to be in contiguous memory,
    ///             and the array of _pointers_ DOES have to be in contiguous
    ///             memory, but the total _storage space_ for the combined total of
    ///             all rows can be in NON-contiguous memory. Again, this is VERY
    ///             different from every other function above.
    /// \param[in]  array_2d    a 2D array; is of type `int * []` (array of ptrs to
    ///                         int) (where each ptr is a sub-array of ints);
    ///                         `int * []` naturally decays to type `int**` (ptr to
    ///                         "ptr to int")
    /// \param[in]  num_rows    The number of rows in the array (number of elements
    ///                         in the `array_2d` outer array)
    /// \param[in]  num_cols    The number of columns in the array (number of
    ///                         elements in each sub-array)
    /// \return     None
    void print_array5(const int* array_2d[], size_t num_rows, size_t num_cols)
    {
        printf("print_array5:\n");
    
        printf("--- Technique 1: use `row_start[col]` ---\n");
        for (size_t row = 0; row < num_rows; row++)
        {
            const int *row_start = array_2d[row]; // VERY DIFFERENT FROM `print_array4` above!
            for (size_t col = 0; col < num_cols; col++)
            {
                // Identical to `print_array4` above.
                printf("array_2d[%zu][%zu]=%i ", row, col, row_start[col]);
            }
            printf("\n");
        }
    
        printf("--- Technique 2: use `array_2d[row][col]` ---\n");
        for (size_t row = 0; row < num_rows; row++)
        {
            for (size_t col = 0; col < num_cols; col++)
            {
                // OR you can simply do this!
                printf("array_2d[%zu][%zu]=%i ", row, col, array_2d[row][col]);
            }
            printf("\n");
        }
    
        printf("\n");
    }
    
    // 2. Basic usage
    print_array5(all_rows, ARRAY_LEN(all_rows), ARRAY_LEN(row1));
    
    // 3. Usage via a pointer
    //
    // 3.1. Easier way: ptr to "ptr to int"; note: `int* array_2d[]` naturally
    // decays to `int**`.
    const int **p5_1 = all_rows;
    print_array5(p5_1, ARRAY_LEN(all_rows), ARRAY_LEN(row1));
    //
    // 3.2. OR this more-complicated way, for the sake of demonstration:
    // ptr to array of 3 `int*`s
    const int* (*p5_2)[ARRAY_LEN(all_rows)] = &all_rows;
    // Explanation: the type of `p5_2` is `int* (*)[3]` (ptr to array of 3
    // int*), so the type of `*p5_2` is `int* [3]` (array of 3 int*), which
    // decays naturally to `int**`, which is what `*p5_2` ends up passing to
    // this function call! So, this call to `print_array5()` here and the one
    // just above are therefore exactly identical!
    print_array5(*p5_2, ARRAY_LEN(all_rows), ARRAY_LEN(row1));
    

구조도 잊지 마세요!

하지만 가끔은 구조물을 사용하는 것이 훨씬 더 쉽다는 것을 잊지 마세요!

예:

typedef struct data_s
{
    int x;
    int y;
} data_t;

// Array of the above struct
data_t data_array[] =
{
    {1, 2},
    {5, 6},
    {7, 8},
};

void print_struct_data(data_t * data, size_t len)
{
    for (size_t i = 0; i < len; i++)
    {
        printf("[data[%zu].x, data[%zu].y] = [%i, %i]\n",
               i, i, data[i].x, data[i].y);
    }
    printf("\n");
}

print_struct_data(data_array, ARRAY_LEN(data_array));

출력:

[data[0].x, data[0].y] = [1, 2]
[data[1].x, data[1].y] = [5, 6]
[data[2].x, data[2].y] = [7, 8]

실행 가능한 완전한 코드:

완전한 실행 가능한 코드로 인해 이 응답은 응답에 허용되는 최대 문자 수인 30000자를 초과합니다.여기서 완전한 코드를 다운로드합니다.c/array_2d_practice.c, eRCaGuy_hello_world repo.

샘플 출력(줄임, 전체 코드를 직접 실행하십시오):

print_array1:
array_2d[0][0]=1 array_2d[0][1]=2
array_2d[1][0]=5 array_2d[1][1]=6
array_2d[2][0]=7 array_2d[2][1]=8

print_array2:
array_2d[0][0]=1 array_2d[0][1]=2
array_2d[1][0]=5 array_2d[1][1]=6
array_2d[2][0]=7 array_2d[2][1]=8

print_array3:
--- Technique 1: ---
array_2d[0][0]=1 array_2d[0][1]=2
array_2d[1][0]=5 array_2d[1][1]=6
array_2d[2][0]=7 array_2d[2][1]=8
--- Technique 2: ---
array_2d_ptr[0][0]=1 array_2d_ptr[0][1]=2
array_2d_ptr[1][0]=5 array_2d_ptr[1][1]=6
array_2d_ptr[2][0]=7 array_2d_ptr[2][1]=8

print_array4:
--- Technique 1: ---
array_2d[0][0]=1 array_2d[0][1]=2
array_2d[1][0]=5 array_2d[1][1]=6
array_2d[2][0]=7 array_2d[2][1]=8
--- Technique 2: ---
array_2d_ptr[0][0]=1 array_2d_ptr[0][1]=2
array_2d_ptr[1][0]=5 array_2d_ptr[1][1]=6
array_2d_ptr[2][0]=7 array_2d_ptr[2][1]=8

print_array5:
--- Technique 1: use `row_start[col]` ---
array_2d[0][0]=1 array_2d[0][1]=2
array_2d[1][0]=5 array_2d[1][1]=6
array_2d[2][0]=7 array_2d[2][1]=8
--- Technique 2: use `array_2d[row][col]` ---
array_2d[0][0]=1 array_2d[0][1]=2
array_2d[1][0]=5 array_2d[1][1]=6
array_2d[2][0]=7 array_2d[2][1]=8

Don't forget about just using structs and arrays of structs instead, which
is sometimes much easier!

[data[0].x, data[0].y] = [1, 2]
[data[1].x, data[1].y] = [5, 6]
[data[2].x, data[2].y] = [7, 8]

참고 자료:

  1. 사용한 주요 레퍼런스: "C 어레이의 유형 안전성 강화"에 대한 정보와 다음과 같은 어레이에 대한 1D 포인터를 해석하고 읽는 방법에 대한 내 답변이 포함되어 있습니다.int (*a)[2]: 배열을 인수로 C의 함수에 전달
  2. https://www.geeksforgeeks.org/pass-2d-array-parameter-c/

연관된:

  1. [내 답변] Arduino 스택 교환: 구조체 배열 초기화 중
  2. [이 답변을 참조하는 내 답변]배열 포인터를 내 함수에 전달하다

이 코드는 C 또는 C++에서는 동작하지 않습니다.활자 배열int[4][4]유형의 포인터로 변환할 수 없습니다.int **은 (무엇인가(무엇인가)int *arr[]는 파라미터 선언을 나타냅니다).C로 컴파일 할 수 있었던 것은 단순히 C++ 컴파일러에서 받은 에러 메시지와 기본적으로 같은 형식의 C 컴파일러 경고를 무시했기 때문일 수 있습니다(C 컴파일러가 본질적인 에러에 대해 경고를 발행하는 경우도 있습니다).

그러니까, 다시 말하지만, 사실이 아닌 주장은 하지 마세요.는 C에서는 하지 않습니다. 2D 를 1D로 int **할 수 .

다차원 배열을 c++의 포인터로 변환하는 중

(승인된 답변을 참조하십시오.문제는 똑같습니다.)

EDIT: 인쇄 코드의 다른 버그가 어레이 패스의 버그의 영향을 위장하고 있기 때문에, 코드는 C 로 동작하고 있는 것처럼 보입니다.의 요소에 적절히 접근하기 위해int **은요, '', '아예, 아예, 아예, 아예, 아예, 아예, 아예.*(*(arr + i) + j) 「」보다 .arr[i][j](일부러).*그 결과, 어레이의 내용과는 전혀 관계가 없는 것을 인쇄하게 되었습니다., 다시 ᄃ, ᄂ, ᄂ, ᄂ, ᄂ에서 .mainC 로 인쇄하고 있는 결과가, 의도한 어레이의 컨텐츠와는 전혀 관계가 없는 것을 확인할 수 있습니다.

경우, 이 항목을 변경할 수 있습니다.printf위에서 설명한 바와 같이 처음에 설명한 어레이 패싱 버그로 인해 코드가 크래시 될 가능성이 높습니다.

더: 한 번 더 할 수 .int[4][4]int **에서 C 것은 .C++가 에러 메세지로 통지하는 것은 다음과 같습니다.C 컴파일러가 알려준 내용입니다만, 「경고」이기 때문에, 아마 무시해 버렸을 것입니다.

문제는...

int a[4][4];

실제로 물리적으로 연속적인 메모리에 저장됩니다.따라서, 4x4 어레이의 임의의 부분에 액세스 하려면 , 「인쇄」기능이 어레이의 치수를 인식할 필요가 있습니다.예를 들어, 다음의 작은 코드 조각은, 2개의 다른 방법으로 메모리의 같은 부분에 액세스 합니다.

#include <iostream>

void print(int a[][4]) {
    for (int i = 0; i <4; i++) {
        for (int j = 0; j < 4; j++) {
            //accessing as 4x4 array
            std::cout << a[i][j] <<std::endl;        

            //accessing corresponding to the physical layout in memory
            std::cout <<  *(*(a)+ i*4 + j) << std::endl;  

        }
    }
}

int main() {
    int a[4][4];

    //populating the array with the corresponding indices from 0 to 15
    int m = 0;
    for (int i = 0; i<4; i++) {
        for (int j= 0; j < 4; j++) {
            a[i][j] =  m;
            m++;
        }
    }
    print(a);
}

따라서 메모리 레이아웃은 변경되지 않지만 액세스 방법은 변경됩니다.그것은 체커보드처럼 시각화할 수 있다.

   0  1  2  3
  ----------
0| 1  2  3  4
1| 5  6  7  8
2| 9 10 11 12
3|13 14 15 16

하지만 실제 물리적 기억은 이렇게 생겼습니다.

0*4+0 0*4+1 0*4+2 0*4+3 1*4+0 1*4+1 1*4+2 1*4+3 2*4+1   etc.
-----------------------------------------------------
1      2       3    4     5     6      7     8     9    etc.

c++에서는 어레이의 데이터는 행 단위로 저장되며, 다음 행의 적절한 메모리 오프셋에 도달하기 위해서는 항상 행 길이(이 경우 4)가 필요합니다.따라서 첫 번째 첨자는 어레이가 선언될 때 필요한 스토리지 양만 나타내며 이후 오프셋을 계산하는 데 더 이상 필요하지 않습니다.

하시면 됩니다.int** its 합니다.이치노

#include <stdio.h>
#include <stdlib.h>
void print(int **a, int numRows, int numCols )
{
  int row, col ;
  for( int row = 0; row < numRows; row++ )
  {
    for( int col = 0; col < numCols ; col++ )
    {
      printf("%5d, ", a[row][col]);
    }
    puts("");
  }
}

int main()
{
  int numRows = 16 ;
  int numCols = 5 ;
  int **a ;

  // a will be a 2d array with numRows rows and numCols cols

  // allocate an "array of arrays" of int
  a = (int**)malloc( numRows* sizeof(int*) ) ;

  // each entry in the array of arrays of int
  // isn't allocated yet, so allocate it
  for( int row = 0 ; row < numRows ; row++ )
  {
    // Allocate an array of int's, at each
    // entry in the "array of arrays"
    a[row] = (int*)malloc( numCols*sizeof(int) ) ;
  }

  int count = 1 ;
  for( int row = 0 ; row < numRows ; row++ )
  {
    for( int col = 0 ; col < numCols ; col++ )
    {
      a[row][col] = count++ ;
    }
  }

  print( a, numRows, numCols );
}

다른 관심사는 D3DMATRIX와 같은 구조입니다.

typedef 구조 _D3DMATRIX {유니언 {구조 {플로트 _11, _12, _13, _14;플로트 _21, _22, _23, _24;플로트 _31, _32, _33, _34;플로트 _41, _42, _43, _44;
};플로트 m[4][4];
};} D3DMATRIX;
D3DMATRIX myMatrix;

은 둘 다 할 수 있다는 입니다.myMatrix.m[0][0] 번째 요소에 ) ('첫 번째 요소를 사용할 수 .myMatrix._11같은 요소에도 접근할 수 있습니다. 조합이 비밀이다.

#include<stdio.h>
void print(int (*arr)[4], int s1, int s2) {
    int i, j;
    for(i = 0; i<s1; i++)
        for(j = 0; j<s2; j++)
            printf("%d, ", arr[i][j]);
}

int main() {
    int a[4][4] = {{6}};
    print(a,4,4);
}

편집: 누군가가 이미 이 솔루션을 게시했습니다. 제 잘못입니다.

양쪽 모두 동작하고 있지만 이론적으로는 무효인 버전을 다음에 나타냅니다(아래 참조).

#include <stdio.h>

static void print(int *arr, size_t s1, size_t s2)
{
    size_t i, j;
    printf("\n");
    for(i = 0; i < s1; i++) {
        for(j = 0; j < s2; j++) {
            printf("%d, ", arr[i * s2 + j]);
        }
    }
    printf("\n");
}

int main(void) {
    int a[4][4] = {{0}};
    print(a[0], 4, 4);
    return 0;
}

템플릿을 사용하는 C++ 버전(Notinlist의 답변에서 채택)은 다음과 같습니다.

#include <iostream>
#include <cstring>

using namespace std;

template <size_t N, size_t M>
struct IntMatrix
{
    int data[N][M];
    IntMatrix() { memset(data, 0, sizeof data); }
};

template <size_t N, size_t M>
ostream& operator<<(ostream& out, const IntMatrix<N,M>& m)
{
    out << "\n";
    for(size_t i = 0; i < N; i++) {
        for(size_t j = 0; j < M; j++) {
            out << m.data[i][j] << ", ";
        }
    }
    out << "\n";
    return out;
}

int main()
{
    IntMatrix<4,4> a;
    cout << a;
    return 0;
}

STL즉, STL 컨테이너를 사용할 수 있습니다.vector< vector<int> > - 플레인 배열입니다.

C99를 사용하면

static void print(size_t s1, size_t s2, int arr[s1][s2]) {
    printf("\n");
    for(size_t i = 0; i < s1; i++) {
        for(size_t j = 0; j < s2; j++) {
            printf("%d, ", arr[i][j]);
        }
    }
    printf("\n");
}

라고 부르면

print(4, 4, a);

Robert가 코멘트에서 지적했듯이, 첫 번째 토막은 실제로 정의되지 않은 행동을 포함한다.단, 포인터 산술이 정의되지 않은 동작을 수반하고 있는 경우(컴퓨터를 파괴하지 않는 경우)에도 항상 포인터를 발생시킨다고 가정하면 표준 내의 다른 제약으로 인해 가능한 결과는 단 하나뿐입니다.즉, 이것은 표준이 불필요하게 정의되지 않은 상태로 남겨진 경우입니다.

내가 알기로는, 대체해서

print(a[0], 4, 4);

와 함께

union m2f { int multi[4][4]; int flat[16]; } *foo = (union m2f *)&a;
print(foo->flat, 4, 4);

합법 C로 하겠습니다.

다차원 배열은 메모리의 연속 블록입니다.다음과 같이 할 수 있습니다.

#include <stdio.h>

void pa(const int *a, int y, int x)
{
    int i, j;
    for (i=0;i<y;i++)
    {
        for (j=0;j<x;j++)
            printf("%i", *(a+j+i*x));
        printf("\n");
    }
}

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

    pa(a[0], 4, 3);

    return 0;
}

C++에서도 동작합니다.

보보보의 답을 C++ 버전으로 보여드리고 싶습니다.

int numRows = 16 ;
int numCols = 5 ;
int **a ;

a = new int*[ numRows* sizeof(int*) ];
for( int row = 0 ; row < numRows ; row++ )
{
   a[row] = new int[ numCols*sizeof(int) ];
}

나머지 코드는 보보보 코드와 동일합니다.

#include<stdio.h>
void print(int arr[][4], int s1, int s2) {
    int i, j;
    printf("\n");
    for(i = 0; i<s1; i++) {
        for(j = 0; j<s2; j++) {
            printf("%d, ", *((arr+i)+j));
        }
    }
    printf("\n");
}

int main() {
    int a[4][4] = {{0}};
    print(a,4,4);
}

이것은 효과가 있을 것이다. 내 말은, 일에서 편집하는 것이다.@AndreyT는 당신의 버전이 아직 작동하지 않는 이유를 설명했습니다.

이것이 2D 어레이를 통과시키는 방법입니다.

알기 쉽게 하기 위해 함수 선언에서 두 크기를 모두 지정할 수도 있습니다.

#include<stdio.h>
void print(int arr[4][4], int s1, int s2) {
    int i, j;
    printf("\n");
    for(i = 0; i<s1; i++) {
        for(j = 0; j<s2; j++) {
            printf("%d, ", *((arr+i)+j));
        }
    }
    printf("\n");
}

int main() {
    int a[4][4] = {{0}};
    print(a,4,4);
}

둘 다 잘 될 거예요.

수 있어요.*((arr+i)+j) 쪽인가에a[i][j] 수 없이) (어쩔 수 없이)*(*(arr+i)+j)하고 있는 는,j "th"i.

#include<cstdio>
template <size_t N, size_t M>
struct DataHolder
{
    int data[N][M];
    DataHolder()
    {
       for(int i=0; i<N; ++i)
           for(int j=0; j<M; ++j)
               data[i][j] = 0;
    }
};

template <size_t N, size_t M>
void print(const DataHolder<N,M>& dataHolder) {
    printf("\n");
    for(int i = 0; i<N; i++) {
        for(int j = 0; j<M; j++) {
            printf("%d, ", dataHolder.data[i][j]);
        }
    }
    printf("\n");
}

int main() {
    DataHolder<4,4> a;
    print(a);
}

C99에서 가변장 어레이를 사용하는 것 외에 컴파일 시에 어레이의 크기를 알 수 없는 경우에는 다차원 어레이를 수용하는 함수를 쉽게 작성할 수 없습니다(C-FAQ의 질문 6.19 참조).이 문제를 해결하는 가장 좋은 방법은 동적으로 할당된 메모리를 사용하여 다차원 어레이를 시뮬레이션하는 것입니다.질문 6.16은 이 작업의 세부 사항을 매우 잘 설명합니다.

단답입니다. 프로그램을 다음과 같이 변경할 수 있습니다.

void print(int arr[], int s1, int s2) {
...
printf("%d,", *(a+i + s2*j));
...
print((int*)a,4,4);

이를 위해서는 포인터와 포인터 산술의 차이와 C와 C++의 배열을 설명하는 더 나은 답이 필요합니다.지금은 그 얘기는 하지 않겠습니다.다른 사람?

저는 당신의 코드에 있는 다른 포스터와 같은 점에 놀라지 않았습니다.인쇄 함수 헤더에서 가장 신경이 쓰이는 것은 초기 포인터를 원래대로 되돌릴 생각이 없는 어레이에 대해 이중 방향 지정을 사용한다는 것입니다(사실 포인터는 상수이기 때문에 변경할 수 없습니다).@|V|lad answer는 1차원 또는 2차원을 고정 상수로 설정하여 이 문제를 해결하지만 s1과 s2를 통과하면 무용지물이 됩니다.

모든 것은 네가 정말 무엇을 하고 싶은지에 달려있다.인쇄는 범용 어레이 인쇄 기능입니까, 아니면 일부 어레이 타입 전용입니까?

가장 먼저 해야 할 일은 활자를 맞추는 것이다.어레이 타입에 관해서 C++의 규칙이 C의 규칙과 같은 경우(분명히 동일), 선언이 지정됩니다.

int a[4][4];

그 the a은 ★★★★★★★★★★★★★int [4][4]의 「Decays」(변환됩니다int (*)[4] 「 int」 )에 , (4 「 」 ) 。print 바꿀 print 접속합니다.

void print(int (*arr)[4], int s1, int s2)
{
  int i, j;        
  for(i = 0; i<s1; i++)        
    for(j = 0; j<s2; j++)        
      printf("%d, ", arr[i][j]);        
}        

.arr[i]arr따라서 명시적인 참조 해제 작업을 수행할 필요가 없습니다.

단점은 이다print할 수 크기를 .int Nx4 어레이를 다른 어레이 크기를 처리하려면 다른 방법을 사용해야 합니다.

첫요소의 하고 첫 요소의 주소를 전달하면 .print다음과 같이 오프셋을 수동으로 계산합니다.

int main() {                    
  int a[4][4] = {{0}};                    
  print(&a[0][0],4,4);  // note how a is being passed                  
}  

void print(int *arr, int s1, int s2)  // note that arr is a simple int *
{
  int i, j;
  for (i = 0; i < s1; i++)
    for (j = 0; j < s2; j++)
      printf("%d, ", arr[i * s2 + j]);
}

오늘날 대부분의 시스템은 줄자 주문 시스템입니다.

여기에 다차원 배열도 직렬로 저장된다는 개념을 활용하는 자체 설명 코드가 있습니다.

샘플 작업 코드:

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

void print1DArray(int arr[]){
    cout << "1D array print: " << arr[3] << endl;
}

int get1DIndex(int r, int c, int noOfColumns){
    return r*noOfColumns + c;
}

void print2DArray(int *mat, int noOfColumns){
    cout << "\n-------------------------------\n";
    cout << "1D index generated: " << get1DIndex(1, 2, noOfColumns); // prints 5
    cout << endl << "Inside printAllPaths: " << mat[get1DIndex(1, 2, noOfColumns)];  // prints 5
}



int main(){

    int arr[5] = {0, 1, 2, 3, 4};
    int mat[3][3] = { {0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
    
    print1DArray(arr);
    print2DArray(*mat, 3);

    return 0;
}

언급URL : https://stackoverflow.com/questions/2828648/how-to-pass-a-multidimensional-array-to-a-function-in-c-and-c

반응형