포인터 배열 : 배열에 메모리 주소 값을 저장하는 배열
int* a[3] = {0xf02f1022, 0xaddf1022, 0xf4221124 }
배열 포인터 : 배열을 가리키는 포인터
int (*a)[4] : 한 행마다 열을 4만큼씩 표현하는 2차원 배열
1차원 배열 변수의 값은 해당 배열공간을 가리키는 주소 이므로
int a[10];
int *b = a;
가 가능하다.
파란색 = 옳은 것
빨간색 = 틀린 것
다차원 배열을 함수의 인자로 전달할 때 다음과 같이 전달할 수 있다.
void Ex(int a[2][2]) {
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++)
cout << a[i][j] << " ";
cout << endl;
}
}
void main() {
int ary[2][2]{ 1,2,3,4 };
Ex(ary);
}
하지만 이와 같은 방법은 범용적이지 못하고 한정적이다. 2행 2열 크기의 배열만 전달할 수 있기 때문이다.
---------------------------------------------------------------------------------------------------
void Ex(int a[][]) {
// ditto
}
그렇다면 이렇게 함수를 만드는 경우를 생각할 수 있을 것이다. 하지만 이 경우도 옳지 않다. 왜냐하면 힘수로 배열을 전달할 때, 배열의 첫번째 주소를 전달한다. 매개변수로 받을 변수는 이 주소가 몇 행 몇 열인지 모르기 때문에 컴파일 오류가 발생할 것이다. 따라서 배열에 명시적으로 명시해줘야한다.
---------------------------------------------------------------------------------------------------
void Ex(int a[2][]) {
// ditto
}
다음과 같이 행만 명시한다면, 이 부분도 잘못 됬다. 만약에 2행 2열의 배열이 함수의 인자로 전달되면 매개변수 a는 1행부터 데이터를 몇 번째 열까지 데이터를 받아야 할지 모르기 때문이다.
---------------------------------------------------------------------------------------------------
void Ex(int a[][2]) {
// ditto
}
이와 같은 방법으로 사용하면 된다.
1 2 3 4 5 6 7 8 9 10 인 배열이 Ex함수의 매개변수로 전달된다면
a배열에 저장된 데이터는 {{1,2}, {3,4}, {5,6}, {7,8}, {9,10}}과 같이 표현 할 수 있다.
열에 명시적으로 숫자를 주었기 때문에, 컴파일러가 위와 같이 해석할 수 있다.
void Ex(int (*a)[2]) {
// ditto
}
또는 배열 포인터를 이용한다.
int형 포인터를 저장할수 있는 배열이라는 의미이다.
쉽게 풀어서 말하자면, 2개의 데이터를 가진 배열의 주소를 가리키는 포인터이다.
---------------------------------------------------------------------------------------------------
다차원 배열을 반환한다고 할 때, C에 익숙치 못한 사람은 다음과 같이 생각할 것이다.
int** Ex2() {
static int a[3][3] = { 1,0,0,0,1,0,0,0,1 };
return a;
}
void main() {
int** dm;
dm = Ex2();
}
위 방법은 잘못 됬다. Ex2함수에서 int a[3][3] 자체는 3행 3열의 배열을 가리키는 주소이기 때문에 더블 포인터로 반환 할 수 없다. 다차원 배열들을 표현하기 위해 int a[3][3]와 같이 사용한 것이지, 실제로는 한 메모리 공간에 있는 것이다. (동적으로 다차원 할당하는 경우는 힙안에서 여러 공간에 있을 수 있다.)
---------------------------------------------------------------------------------------------------
int* Ex2() {
static int a[3][3] = { 1,0,0,0,1,0,0,0,1 };
return (int*)a;
}
void main() {
int (*dm)[3];
dm = (int(*)[3])Ex2();
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++)
cout << dm[i][j] << " ";
cout << endl;
}
}
네트워크 프로그래밍을 해본 사람은 다음과 같이 형변환을 이용하여 반환할 수도 있다.
2차원 배열도 실제로는 1차원 배열이 쭉 나열 되있는 것이므로, int형 포인터로 캐스팅후 반환하면 된다. main문에서도 int*형으로 받아야 한다. 2차원 배열로 받을려면 명시적으로 (int(*)[3])와 같이 캐스팅 해줘야한다.
함수 안에 static으로 선언한 이유는 지역 변수는 함수가 끝나면 소멸되기 때문이다. 만약에 static을 사용하지 않고 반환하게 되면 배열의 주소 값이 반환되게 되는데, main문으로 돌아와 이 주소값을 다른 변수에 저장하여도, 이미 이 배열 공간은 함수가 반환되면서 스택에서 소멸되었기 때문이다.
위 방법은 어찌보면 편법에 가깝다. 캐스팅 변환 없이 반환하려면 반환형을 명시적으로 표현 해줘야한다.
---------------------------------------------------------------------------------------------------
int(*Ex2(void))[3]{
static int a[3][3] = { 1,0,0,0,1,0,0,0,1 };
return a;
}
void main()
{
int (*dm)[3];
dm = Ex2();
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++)
cout << dm[i][j] << " ";
cout << endl;
}
}
위와 같이 사용하면 다차원 배열을 반환할 수 있다.
---------------------------------------------------------------------------------------------------
팁으로 (int(*)[3])와 같은 표현은 가독성이 떨어진다. 따라서 다음과 같이 typedef 해주는 것이 좋다.
typedef int(*Dimension)[3];
Dimension Ex2(void){
static int a[3][3] = { 1,0,0,0,1,0,0,0,1 };
return a;
}
void main()
{
Dimension dm;
dm = Ex2();
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++)
cout << dm[i][j] << " ";
cout << endl;
}
}
---------------------------------------------------------------------------------------------------
3차원 이상 배열도 다음과 같은 방법으로 코딩 하면 된다.
int (*Ex2(void))[2][2]{
static int a[][2][2] = { { {1,2} ,{1,2} },{ {1,2} ,{1,2} } };
return a;
}
void main() {
int (*dm)[2][2] = Ex2();
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 2; k++)
cout << dm[i][j][k] << " ";
cout << endl;
}
cout << endl;
}
}
---------------------------------------------------------------------------------------------------
가독성을 생각한다면 다음과 같이 코딩한다.
typedef int(*Dimension)[2][2];
Dimension Ex2(void){
static int a[2][2][2] = { { {1,2} ,{1,2} },{ {1,2} ,{1,2} } };
return a;
}
void main() {
Dimension dm = Ex2();
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 2; k++)
cout << dm[i][j][k] << " ";
cout << endl;
}
cout << endl;
}
}
---------------------------------------------------------------------------------------------------
3차원 배열은 문자열에 많이 사용 된다.
typedef char(*Dimension)[2][10];
Dimension Ex2(void){
static char a[2][2][10] = { { "abcd" , "qqqq" },{ "adzoo" , "vvvv" } };
return a;
}
void main() {
Dimension dm = Ex2();
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
cout << dm[i][j] << endl;
}
cout << endl;
}
}
---------------------------------------------------------------------------------------------------
이와 같은 형식들은 외우는 것이 아니다. 포인터에 대한 메모리 관점에서 생각해보면 전혀 외울 필요가 없을 것이다.