첫번째 실험. 1차원 배열을 매개 변수로 넘기기
// // arrayTest.cpp // BJ // // Created by 신기열 on 21/10/2019. // Copyright © 2019 신기열. All rights reserved. // #include <stdio.h> #include <iostream> using namespace std; // array[] 형식으로 넘기기 void Array(int a[]){ cout << "------------------- aaray[]로 넘긴 배열 a -------------------" << '\n'; int i = 0; while(i != 12){ printf("a[%d]의 값 : %d ", i, a[i]); printf("a[%d]의 주솟값 : %x\n", i, &a[i]); i++; } cout << "----------------------------------------------------------" << '\n'; } // *array 형식으로 넘기기 void Array2(int *a){ cout << "-------------------- *array로 넘긴 배열 a --------------------" << '\n'; int i = 0; while(i != 12){ printf("a[%d]의 값 : %d ", i, a[i]); printf("a[%d]의 주솟값 : %x\n", i, &a[i]); i++; } cout << "----------------------------------------------------------" << '\n'; } // array[10] 형식으로 넘기기 void Array3(int a[0]){ cout << "------------------ array[10]로 넘긴 배열 a ------------------" << '\n'; cout << " 이 경우 array[0], array[1] ... array[10]으로 넘길 때 모두 같은" << '\n'; cout << " 결과가 나온다." << '\n'; int i = 0; while(i != 12){ printf("a[%d]의 값 : %d ", i, a[i]); printf("a[%d]의 주솟값 : %x\n", i, &a[i]); i++; } cout << "----------------------------------------------------------" << '\n'; } int main(){ int a[10]; for(int i = 0; i < 10; i++){ a[i] = i; } cout << "------------------- main 함수에서의 배열 a -------------------" << '\n'; int i = 0; while(i != 12){ printf("a[%d]의 값 : %d ", i, a[i]); printf("a[%d]의 주솟값 : %x\n", i, &a[i]); i++; } cout << "----------------------------------------------------------" << '\n'; Array(a); Array2(a); Array3(a); }
array[], *array, array[10], array[0]으로 넘겼을 때 모두 같은 결과가 나왔다. 어떤 방식으로 하든 일단 주솟값을 넘기는 것이므로 똑같이 나오게 된다.
a 배열은 10개의 값을 가지고 있어서, 범위를 초과하면 당연히 쓰레기 값이 출력된다. 그렇다면 매개변수로 넘긴 배열의 index의 범위를 알고 싶다면? 그런거 없다. 배열의 범위를 애당초 알고 있거나, 배열에 들어갈 수 있는 수의 조건을 알고 있거나, 가변 길이를 원한다면 벡터를 사용하도록 하자. 템플릿을 수정해서 직접 구현하는 방법이 있다는데, 이 경우는 벡터를 쓰는 것과 다를 바가 없다. 벡터를 쓰자!
두번째 실험. 다차원 배열을 매개 변수로 넘기기
// // arrayTest2.cpp // BJ // // Created by 신기열 on 21/10/2019. // Copyright © 2019 신기열. All rights reserved. // #include <stdio.h> #include <iostream> using namespace std; void Array(int a[][3]){ cout << "------------------- array[][3]로 넘긴 배열 a -------------------" << '\n'; int i = 0; while(i != 4){ int j = 0; while(j != 4){ printf("a[%d][%d]의 값 : %d ", i, j, a[i][j]); printf("a[%d][%d]의 주솟값 : %x\n", i, j, &a[i][j]); j++; } i++; } cout << "----------------------------------------------------------" << '\n'; } void Array2(int (*a)[3]){ cout << "------------------- (*array)[3]로 넘긴 배열 a -------------------" << '\n'; int i = 0; while(i != 4){ int j = 0; while(j != 4){ printf("a[%d][%d]의 값 : %d ", i, j, a[i][j]); printf("a[%d][%d]의 주솟값 : %x\n", i, j, &a[i][j]); j++; } i++; } cout << "----------------------------------------------------------" << '\n'; } void Array3(int a[3][3]){ cout << "------------------- array[3][3]로 넘긴 배열 a -------------------" << '\n'; int i = 0; while(i != 4){ int j = 0; while(j != 4){ printf("a[%d][%d]의 값 : %d ", i, j, a[i][j]); printf("a[%d][%d]의 주솟값 : %x\n", i, j, &a[i][j]); j++; } i++; } cout << "----------------------------------------------------------" << '\n'; } void Array4(int a[][]){ cout << "------------------- array[][]로 넘긴 배열 a -------------------" << '\n'; cout << "컴파일 불가능" << '\n'; int i = 0; while(i != 4){ int j = 0; while(j != 4){ printf("a[%d][%d]의 값 : %d ", i, j, a[i][j]); printf("a[%d][%d]의 주솟값 : %x\n", i, j, &a[i][j]); j++; } i++; } cout << "----------------------------------------------------------" << '\n'; } void Array5(int (*a)[]){ cout << "------------------- (*array)[]로 넘긴 배열 a -------------------" << '\n'; cout << "컴파일 불가능" << '\n'; int i = 0; while(i != 4){ int j = 0; while(j != 4){ printf("a[%d][%d]의 값 : %d ", i, j, a[i][j]); printf("a[%d][%d]의 주솟값 : %x\n", i, j, &a[i][j]); j++; } i++; } cout << "----------------------------------------------------------" << '\n'; } void Array6(int (*a)[3][4]){ cout << "------------------- (*array)[3][4]로 넘긴 배열 a -------------------" << '\n'; cout << "컴파일 불가능" << '\n'; int i = 0; while(i != 2){ int j = 0; while(j != 3){ int k = 0; while(k != 4){ printf("a[%d][%d][%d]의 값 : %d ", i, j, k, a[i][j][k]); printf("a[%d][%d][%d]의 주솟값 : %x\n", i, j, k, &a[i][j][k]); k++; } j++; } i++; } cout << "----------------------------------------------------------" << '\n'; } int main(){ int a[3][3]; int a2[2][3][4]; for(int i = 0; i < 3; i++){ for(int j = 0; j < 3; j++){ a[i][j] = i + j; } } for(int i = 0; i < 2; i++){ for(int j = 0; j < 3; j++){ for(int k = 0; k < 4; k++){ a2[i][j][k] = i + j + k; } } } Array(a); Array2(a); Array3(a); Array4(a); Array5(a); Array6(a2); return 0; }
일단 Array4와 Array5 에서 오류가 발생하여 컴파일이 안된다. (둘을 빼고 컴파일을 하면 1차원 배열과 같이 같은 값, 같은 주솟값으로 잘 나오게 된다.) 오류 내용은 다음과 같다.
Array4
Array5
SubScript of pointer to incomplete type 'int []'
제대로 된 타입이 아닌 형태를 이용했다는 것인데, 배열을 매개 변수로 보내는 기본적인 형태는 *array이다.
이 때 C나 C++ 언어에서는 array[]나 array[][size] 같은 형태도 허용하는데, 매개 변수를 배열 형태로 보낸다고 해도 배열의 포인터로써 사용하겠다는 의미이며, 이 때 대괄호 안에 어떤 수가 들어가든 대괄호의 내용은 그냥 무시한다.
(어차피 배열의 포인터를 쓰고자하는 의미니까) 그래서 1차원 배열에서는 *arr = arr[] = arr[0] = arr[1] = ... = arr[10]이 다 같은 결과가 나오는 것이다. 그렇다면 2차원 배열에서의 array[][]는?
array의 포인터를 이용하겠다는 의미는 통할지라도, 그 그룹 안에 얼마 만큼 공간이 들어가는지를 선언해주지 않았기 때문에 오류가 나오는 것이다.
가령,
int array[][4] = [[int], [int], [int], [int]][[int], [int], [int], [int]].... 이런 식으로 되지만,
int array[][] = [[int], [int], [int], [int]......][[int], [int], [int], [int]......] 이런 식으로 되기 때문에 안에 들어가는 공간을 특정할 수 없기 때문에 불가능한 것이다. 사실상 Array4 와 Array5에서 생기는 오류는 같은 의미를 가지게 된다.
int array[][4] = [[int], [int], [int], [int]][[int], [int], [int], [int]].... 이런 식으로 되지만,
int array[][] = [[int], [int], [int], [int]......][[int], [int], [int], [int]......] 이런 식으로 되기 때문에 안에 들어가는 공간을 특정할 수 없기 때문에 불가능한 것이다. 사실상 Array4 와 Array5에서 생기는 오류는 같은 의미를 가지게 된다.
그렇다면 3차원 배열을 어떻게 매개변수로 넘겨주는가? 조금 응용해보면 알 수 있다. (*array)[size][size]와 같이 안에 들어가는 차원들의 size를 정해준 후 넘겨주면 되는 것이다.
세번째 실험. 배열과 재귀함수
// // arrayTest3.cpp // BJ // // Created by 신기열 on 21/10/2019. // Copyright © 2019 신기열. All rights reserved. // #include <stdio.h> #include <iostream> using namespace std; void recursive1(int *a1, int cnt){ if(cnt == 3) return; for(int i = 0; i < 5; i++){ a1[i] = a1[i] + a1[i]; } for(int i = 0; i < 5; i++){ cout << a1[i] << " "; } cout << '\n'; recursive1(a1, cnt + 1); } void recursive2(int (*a2)[4], int cnt){ if(cnt == 3) return; for(int i = 0; i < 4; i++){ for(int j = 0; j < 4; j++){ a2[i][j] = a2[i][j] + a2[i][j]; } } for(int i = 0; i < 4; i++){ for(int j = 0; j < 4; j++){ cout << a2[i][j] << " "; } cout << '\n'; } cout << '\n'; recursive2(a2, cnt + 1); } void recursive3(int (*a3)[3][3], int cnt){ if(cnt == 3) return; for(int i = 0; i < 3; i++){ for(int j = 0; j < 3; j++){ for(int k = 0; k < 3; k++){ a3[i][j][k] = i + j + k; } } } for(int i = 0; i < 3; i++){ for(int j = 0; j < 3; j++){ for(int k = 0; k < 3; k++){ cout << a3[i][j][k] << " "; } cout << '\n'; } cout << '\n'; } cout << '\n'; recursive3(a3, cnt + 1); } int main(){ int a1[5]; int a2[4][4]; int a3[3][3][3]; for(int i = 0; i < 5; i++){ a1[i] = i; } for(int i = 0; i < 4; i++){ for(int j = 0; j < 4; j++){ a2[i][j] = i + j; } } for(int i = 0; i < 3; i++){ for(int j = 0; j < 3; j++){ for(int k = 0; k < 3; k++){ a3[i][j][k] = i + j + k; } } } cout << "----------------1차원 배열-------------" << '\n'; recursive1(a1, 0); cout << "=====================================" << '\n'; cout << "----------------2차원 배열-------------" << '\n'; recursive2(a2, 0); cout << "=====================================" << '\n'; cout << "----------------3차원 배열-------------" << '\n'; recursive3(a3, 0); cout << "=====================================" << '\n'; return 0; }
일단 1차원이든 2차원이든 3차원이든 간에, 혹은 배열을 재귀함수 내에서 선언해서 돌리든, 밖에 미리 배열을 생성해서 돌리든 이 또한 별 문제는 없다. 결국 배열의 차원과 재귀함수는 큰 관계가 없었다. 그렇다면 그 때 왜 segmentation fault가 났는가하면 재귀함수의 호출의 횟수 때문이었다.
호출 횟수가 10000번 정도면 상관이 없어지지만, 10만번이 넘어가게 되면 스택에 들어가있는 함수의 개수가 너무 많아지기 때문에 stack overflow가 발생하는 것이었다. 이래서 DFS를 사용할 때 깊이가 너무 깊어지지 않은지를 잘 생각해봐야한다.
배열 넘기는 법이 정립된 것 같네요 감사합니다!
답글삭제댓글을 이제 봤네요.. 읽어주셔서 감사드립니다 ^^
삭제