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 배열을 함수에 전달하는 세 가지 방법이 있습니다.
매개 변수는 2D 배열입니다
int array[10][10]; void passFunc(int a[][10]) { // ... } passFunc(array);
매개 변수는 포인터를 포함하는 배열입니다
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);
매개 변수는 포인터에 대한 포인터입니다.
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
'Programing' 카테고리의 다른 글
Objective-C가 @property 속성을 선언했습니다 (비 원자, 복사, 강, 약) (0) | 2020.03.20 |
---|---|
가운데에 하나의 유연한 열이있는 두 개의 고정 너비 열을 어떻게 가질 수 있습니까? (0) | 2020.03.20 |
bash 명령 행 인수에 액세스 $ @ vs $ * (0) | 2020.03.20 |
객체를 문자열로 직렬화 (0) | 2020.03.20 |
모든 자식 푸시에서 사용자 이름과 비밀번호를 지정하지 않으려면 어떻게합니까? (0) | 2020.03.20 |