[ 다차원 배열 ] |
다차원 배열이란 ?
2차원 이상의 배열을 의미하는 것이다. 2차원배열, 3차원배열 등등
2차원 배열 선언과 동시에 초기화
int main(void) { //요소 전체 초기화 int buila[3][3] = { { 1, 2, 3 }, 1 2 3 { 4, 5, 6 }, => 4 5 6 { 7, 8, 9 } 7 8 9 };
//일부 요소만 초기화, 이런경우 부족한부분은 0으로 채워진다. int buila2[3][3] = { { 1 }, 1 0 0 { 4, 6 }, => 4 6 0 { 7, 8, 9} 7 8 9 };
//1차원 배열 초기화 형태를 취하는경우, 초기화 리스트의 수가 배열요소의 수보다 적은경우에는 나머지 요소들은 0으로 초기화 된다. 1 2 3 int buila3[3][3] = { 1, 2, 3, 4, 5, 6, 7 }; => 4 5 6 7 0 0
//배열의 크기를 알려주지 않고 초기화하기, 최소한 한개의 인덱스는 가지고 있어야한다. 첫번째 인덱스만 생략 가능하다. 2 x 4 or 4 x 2
int buila4[][4] = { 1, 2, 3, 4, 5, 6, 7, 8 };
.... } |
2차원 배열 이름의 포인터 타입
위의 코드에서 보듯이
a[0] == a는 같다,
a[0] == a[0][0]은 같다. a[0]은 a[0][0]을 가르킨다.
a[1] == a[1][0]은 같다. a[1]은 a[1][0]을 가르킨다.
a[2] == a[2][0]은 같다. a[2]은 a[2][0]을 가르킨다.
─────────────────────────────────────────────
2차원 배열 이름을 이용한 포인터 연산
위의 코드를 보면
a는 a[0][0]을 가르킨다.
a+1는 a[1][0]을 가르킨다.
a+2는 a[2][0]을 가르킨다.
즉!
a == a[0] 같다. a[0]은 a[0][0]을 가르킨다. a+1 == a[1] 같다. a[1]은 a[1][0]을 가르킨다. a+2 == a[2] 같다. a[2]은 a[2][0]을 가르킨다. |
"2차원 배열 이름은 포인터 연산 시 행 단위로 이동한다."
"int a[3][2]; // (4바이트 * 2) 행단위로 차이가 난다 "
"int a[2][3]; // (4바이트 * 3) 행단위로 차이가 난다."
자 이제 위의 내용을 정리해보면 배열이름의 포인터 타입이 나온다.
int arr[2][4]; => int (*pArr)[4];
읽어보면,
int형 변수를 가르키고 포인터연산시 4칸씩 이동하는 pArr포인터
위에 [4]는 포인터 연산에 따른 증가 혹은 감소의 폭을 말한다.
포인터 연산에 의해 값이 1 증가 및 감소 될경우 4칸씩(행단위 이동이니까) 이동하는 포인터라는 것
pArr이 가리키는 대상이 int형 데이터이므로 (4 * 4) 이동하게 될것이다.
─────────────────────────────────────────────
함수 인자로 다차원 배열 전달하기
[코드]
[결과]
위의 코드는?
int (*ptr)[4] 포인터 타입으로 받을수있는 서로다른 2차원배열의 요소값을 다중for문을 이용해서 출력하는 예제 입니다.
여기서 잠깐!!
int (*pArr)[4]; //int형 변수를 요소로 지니고 포인터 연산시 4칸씩 이동하는 2차원 배열을
가리키는 포인터
int* pArr[4]; //int형 변수의 주소값 4개를 저장할 수 있는 배열
int* arr1[5]; => int** p1; //1차원 배열이니까 포인터에 포인터선언 int* arr2[3][5]; => int* (*p2)[5]; //2차원 배열이니까 (*p2)선언 및 포인터 연산시 [5]칸 int** arr3[5]; => int*** p3; int*** arr4[3][5] => int*** (*pArr)[5]; |
해깔린다. 명백히 해놓자.
[ 다중 포인터 ] |
다중포인터란?
포인터의 포인터 혹은 더블포인터라 불리는 포인터이다.
ex ) int ** pP;
포인터는 기본적으로 그 대상 변수의 양(사이즈)이 많거나, 함수안에서 사용하는 목적으로 만듭니다.
(어디까지나 나의 생각 기준 입니다.)
그럼 다중포인터는 왜 필요해서 쓰는걸까?
예를 들어봅시다.
void malloc_Func(int *x) { x = (int*)malloc(sizeof(int)*100); }
void main() { int *p = NULL; malloc_Func(p); //문제점!! } |
위의 코드는 함수에서, 호출한 쪽의 포인터에 메모리 할당을 하기 위해서 간단하게 코딩한것입니다.
언뜻보기엔 문제가 없어 보이지만, 이 코드에는 문제가 있습니다.
간단하게 정리하자면, int* p 와 int * x 는 각각의 포인터 변수이고, 매개변수로 p가 전달됨으로써
x는 p가 가르키는 (NULL)을 똑같이 가르킬 뿐이지, 각각의 포인터라는 뜻입니다.
위의 코드대로 x에 메모리 할당을 해봤자, 각각의 포인터 변수이니까 p는 할당이 안되는것이죠
즉 같은 곳을 가르키는 전혀다른 포인터 변수 라는것입니다. 이걸 그냥 복사본이라고 하죠
int *x 는 int * p 하고는 상관이 없는 것입니다.
그럼 왜 복사본이 되는것일까?
이유는 간단합니다. 포인터 변수에다가 포인터 변수를 담았으니, 주소값이 대입이 이루어집니다.
변수는 다르지만, 같은것을 가르키게 되는것이죠. Call-By-Value
그럼 이문제를 어떻게 해결하면 좋을까요?
우리는 int * p 를 x 가 참조하여 x 를 통해 p를 변경하면 가능합니다.
쉽게 말해 x가 p를 가르키게 만들면, x에다가 할당하면 p가 할당이 되는 것입니다. x가 p의 주소값을 가르키기 때문에 x에다가 동적 할당 하면, 결국 p의 주소에 메모리가 할당 되는것이죠
자 그럼 더블 포인터에 의한 Call-By-Reference 바꿔보죠
void malloc_Func(int **x) // main()의 p 변수 자체를 가리키는, "p에 대한 포인터". { *x = (int*)malloc(sizeof(int)*100); // *x가 p와 같음. main()의 p 가 새 메모리를 가리키게 함. } void main() { int *p = NULL; malloc_Func(p); } |
위의 코드에서 더블포인터를 사용해서 메모리 할당을 하였습니다.
왜냐? p 변수 자체를 가르키는 포인터를 정의 하기위해서 더블포인터를 사용한것이죠.
이렇게 되면, 매개변수 x는 p가 가르키는 (NULL)을 가르키게 되는게 아니라,
포인터변수 p를 가르키게 되겠죠.
즉 매개변수 x로 p를 바꿀수있게 되는것입니다.(*x == p) 가 되는것이죠.
그럼 main함수에 있는 포인터 p는 함수에서 할당된 새메모리를 가르키게 되는것입니다.
더블포인터를 사용해서 깔끔하게 수정이 됐습니다.
다중,더블포인트는 쓰이는데는 여러가지가 있습니다.
하지만, 쓰이는곳이 조금씩 틀릴뿐이지, 여기서 크게 의미가 바뀌는 부분은 없습니다.
'0x0001 > C, C++' 카테고리의 다른 글
[C언어] 메모리 관리와 동적할당 (0) | 2019.02.08 |
---|---|
[C언어] 함수 포인터, void형 포인터 (0) | 2019.02.07 |
[C언어] const 키워드 (0) | 2019.02.07 |
[C언어] extern "C" (0) | 2019.02.07 |
[C언어] 재귀적 함수, 가변인자 함수 (0) | 2019.02.07 |