티스토리 뷰
이중 포인터를 사용하는건 주로 포인터의 동적 배열을 나타내기 위해서 이다.
우선 아래와 같은 클래스가 하나 있다고 가정 해보자.
class CTest
{
public:
CTest() {}
{
public:
CTest() {}
int a;
};
};
이녀석에 대한 포인터를 선언하고 객체 하나를 생성하려면 어떻게 해야할까?
다음과 같이 써주면 된다.
CTest *pTest = new CTest;
이렇게 하면 pTest는 CTest의 객체를 참조한다.
근데 이제 CTest객체가 여러개 필요한 상황이 나오면 어떻게 해야할까
처음에 아무 생각없이 아래와 같이 했었다.
CTest *pTest1 = new CTest;
CTest *pTest2 = new CTest;
...
CTest *pTest2 = new CTest;
...
필요한게 정해져있다면 뭐 저래도 괜찮겠지만.. 그래도 매직넘버를 늘리는건 좋지 않다..
그래서 좋은 방법중 하나는 배열로 생성하는 방법이다.
배열로의 생성은 아래와 같이 한다.
CTest aTest[3];
가장 무난하다.
그리고 이와 대응 되도록 포인터를 3개 가진 변수를 선언하려면 다음과 같이 한다.
CTest *apTest[3]; //pointer의 array이기 때문에 ap라고 지었다.
apTest[0] = new CTest;
apTest[1] = new CTest;
apTest[2] = new CTest;
apTest[0]->a = 1;
apTest[1]->a = 2;
2[apTest]->a = 3; //이런식으로 써도 상관없다 ㅋㅋㅋ
apTest[1] = new CTest;
apTest[2] = new CTest;
apTest[0]->a = 1;
apTest[1]->a = 2;
2[apTest]->a = 3; //이런식으로 써도 상관없다 ㅋㅋㅋ
printf("%d %d %d",apTest[0]->a, apTest[1]->a, apTest[2]->a);
delete apTest[0];
delete apTest[1];
delete apTest[2];
//힙에 객체가 할당 되었으므로 할당된 갯수만큼 지워줘야함을 잊지 말 것
위의 것은 apTest[0]과 apTest[1]과 apTest[2] 가 들어갈 수 있는 배열 공간 *apTest[3]을 선언을 해놓고, 각각에다가 객체를 참조시켜준 것이다. 즉 apTest[0]은 CTest의 객체를 참조한다.delete apTest[1];
delete apTest[2];
//힙에 객체가 할당 되었으므로 할당된 갯수만큼 지워줘야함을 잊지 말 것
다른 방법으로 아래와 같은 방법도 있다.
CTest *paTest = new CTest[10]; //포인터를 이용한 어레이라서 pa라고 붙엿다.
paTest[0].a = 1;
(paTest+1)->a = 2; //우왕 이런식으로도된다.
paTest[125].a = 142;
printf("%d %d %d",paTest[0].a, 1[paTest].a, paTest[125].a);
delete[] paTest;
(paTest+1)->a = 2; //우왕 이런식으로도된다.
paTest[125].a = 142;
printf("%d %d %d",paTest[0].a, 1[paTest].a, paTest[125].a);
delete[] paTest;
희한하지 않은가? 위는 동적으로 생성한 변수이다. 위처럼 동적으로 생성한 경우에는 런타임 시점에 크기를 계산 할 수가 없다.
따라서 현재 125번째 자리에도 자리 잡아놓았는데 저렇게 해도 잘 된다! 왜냐면 인덱스가 몇까지인지 모르니까~
그래서 sizeof(paTest)를 하면 겨우 4가 나올 뿐이다.(32bit cpu기준) 근데 절대로 할당하지 않은 메모리를 참조하는 바보같은 짓은 하지 말자 -_-;; 어짜피 컴파일 시점에서 aTest[125]는 *(paTest+125)로 바뀐다.(이에 대한 재밌는 얘기를 글의 후반에 작성하겠음)
위의 구문을 보면
CTest *paTest = new CTest[10]; 으로 해놨기 때문에 힙에 할당하는 값은 sizeof(CTest*) * 10 이다. 따라서 paTest에 CTest에 대한 배열 10개가 생겼다! 라고 생각하면 된다.(그래도 sizeof하면 sizeof(CTest*)값 밖에 안나온다. 위에서 말했듯이 동적이라서!)
음? CTest *paTest = new CTest[];나 CTest *paTest = new CTest; 로 했어도 paTest[133].a = 3; 이런식으로 선언한 영역을 넘어서는 참조가 가능하다????? (일부 컴파일러에서 가능)
만약 CTest *paTest = new CTest[]; 로 했다면 힙에 할당하는 값은 sizeof(CTest*) 뿐이며 참조는 머나먼곳까지 가능하다. 하지만 이 값은 보장될 수 없는 메모리에 쓰여지기 때문에 매우 위험하다. 만약 이런식으로 선언을 했다해도 이것은 배열이 만들어진게 아니다... 그리고 paTest[30].a 뭐 이런식으로 사용한다고해도.. 이건 절대로 배열이 아니다. 걍 (paTest+30)->a 일 뿐이다. 그냥 어딘지 모르는 번지를 참조하고 있을 뿐이다. 그래서 위와 같이 했을 경우 변수 참조가 아닌 함수 호출을 시도했을 경우 런타임 에러가 나면서 종료된다. 근데 버그인지 뭔지 모르겠지만 CTest *paTest = new CTest[10]; 이런식으로 선언했을때는 paTest[1000].func(); 이런식으로 함수 호출을 했을때 호출이 된다 -_-;; 짱.. C++ 젠장..
그러니까 이런 방법 -'[]로 동적 배열 잡는 멍청한 짓'은 하지 말자 -_-;;
아 그리고 CTest* 임에도 불구하고 이름을 pTest가 아닌 paTest로 잡은 이유는 우리가 이것을 배열처럼 사용할 것이기 때문이다.
참고로 CTest *pTest = new CTest; 일 때
pTest->a와 pTest[0].a 는 동치라는 점을 명심하자.
(pTest+2)->a와 pTest[2].a 역시 동치이다. 물론 그냥 쓰면 사용 안되고, 객체를 할당해 놓고 사용해야겠다. 그럴때 pa라고 붙여서 쓰자!!
CTest *paTest.. 이렇게 *이 붙어서 꼭 포인터 처럼 보이지만 p를 붙여서 컨벤션을 쓸꺼면 보통 객체 하나만 가리킬때 쓰기 때문에 헷깔리지 않기 위하여 pa를 붙이기로 했다. 즉 paTest는 배열의 첫번째 값을 가리킬 뿐이다. 우리는 실제로 paTest[0]...paTest[2]... 이런식으로 사용 할 것이기 때문에 pa를 붙이는게 더 낫다.
즉 저 명령은 그냥, aTest의 번지에 new CTest[10]; CTest의 객체를 참조하기 위한 공간 10개를 할당 하는 것이다.
다음 것도 보자.
CTest **ppTest; //이놈이 바로 포인터의 포인터
ppTest = new CTest*[3]; //포인터를 저장할 공간을 할당해야한다. 뭔가 아이러니하다.
ppTest[0] = new CTest; //ppTest[0]을 pTest 하나라고 생각하면 이해가 쉬울 것이다.
ppTest[1] = new CTest;
ppTest[2] = new CTest;
ppTest = new CTest*[3]; //포인터를 저장할 공간을 할당해야한다. 뭔가 아이러니하다.
ppTest[0] = new CTest; //ppTest[0]을 pTest 하나라고 생각하면 이해가 쉬울 것이다.
ppTest[1] = new CTest;
ppTest[2] = new CTest;
ppTest[0]->a = 3;
delete ppTest[0];
delete ppTest[1];
delete ppTest[2];
delete[] ppTest; //new가 4개니까 이렇게 해준다.
이해가 가는가?
포인터의 포인터이기 때문에 포인터를 위한 공간도 할당해야한다. ppTest = new CTest*[3]; 바로 이런식으로!
ppTest = new CTest*[3]; 이 구문은 CTest* 이라는 자료형을 3개 동적으로 생성하겠다는 말이다.
그러니까, ppTest는 CTest*을 3개 갖게 된다.
이제 위에서 만든 CTest*에 직접적인 CTest 객체를 할당할 차례다. 그것은 다음과 같이
ppTest[0] = new CTest; 해준다.
ppTest[0]이 바로 CTest* 인 것이다.
마지막으로 하나 더 굉장한 것을 보자. 머리가 아프다면 이 이상은 보지 않기를 권장함 -.-;;
CTest **ppaTest;
ppaTest = new CTest*[3];
ppaTest[0] = new CTest[5];
ppaTest[1] = new CTest[3];
ppaTest[2] = new CTest[4];
ppaTest = new CTest*[3];
ppaTest[0] = new CTest[5];
ppaTest[1] = new CTest[3];
ppaTest[2] = new CTest[4];
(ppaTest[2]+3)->a = 3;
ppaTest[1][2].a = 4;
ppaTest[0]->a = 1;
ppaTest[1][2].a = 4;
ppaTest[0]->a = 1;
printf("%d %d %d",ppaTest[0][0].a, ppaTest[1][2].a, ppaTest[2][3].a);
delete[] ppaTest[0];
delete[] ppaTest[1];
delete[] ppaTest[2];
delete[] ppaTest;
delete[] ppaTest[1];
delete[] ppaTest[2];
delete[] ppaTest;
아아.. 이제 머리가 아프다..
차근 차근 봐보자
CTest **ppaTest;가 있다. CTest의 이중 포인터이다.
ppaTest = new CTest*[3] 을 해서 CTest*가 들어갈 공간 3개를 할당 하였다.
다음을 보면..
ppaTest[0] = new CTest[5];
즉.. ppaTest[0]은 자료형이 CTest* 인데, 이 자리에 CTest를 5개 동적으로 할당한 것이다.
즉 ppaTest[0]은
ppaTest[0][0], ppaTest[0][1], ppaTest[0][2], ppaTest[0][3], ppaTest[0][4] 이렇게 4개의 영역을 갖게 된다..
다른놈들도 마찬가지.....
아 머리가 깨진다.....
그래서 오늘의 결론은..
....
걍 벡터 쓰자 -_-;;
자 이제 위에서 말한 재밌는 이야기를 해주겠다.
위에서 보면
pTest[10]을 10[pTest] 처럼 쓴 부분을 볼 수 있다. 어째서 이것이 가능한가?
pTest[x]는 결국 컴파일러에 의해서 *(pTest + x)가 된다.
마찬가지로 x[pTest]는 컴파일러에 의해서 *(x + pTest)가 된다.
즉 pTest[x]와 x[pTest]가 똑같기 위한 조건은 덧셈(+)에 대해서 교환법칙이 성립한다는 것이다.
pTest[10]을 10[pTest]처러 쓸 수 있는 이유!!!!!! -> 덧셈에 대해서 교환 법칙이 성립하기 때문에.
그럼 이만~~~~~~~~~~~~~
p.s> kering님 감사
'Language > C C++' 카테고리의 다른 글
l + (r - l) / 2 혹은 start + (end - start) / 2 에 대한 고찰 (1) | 2020.07.26 |
---|---|
Visual Studio Code에서 C/C++ 컴파일 하기 (0) | 2018.03.04 |
[C++] 2차원 배열의 포인터. 배열은 포인터 상수다. (5) | 2011.03.24 |
[C++] class를 주고 받을땐 레퍼런스를 사용하자. (0) | 2011.01.28 |
[C++] 분기문을 싫어하는 나같은 사람에겐.. (0) | 2010.11.30 |
[C++] RTTI와 vtable 그리고 RTCI (0) | 2010.11.25 |
[C++] dynamic_cast, const_cast, reinterpret_cast, static_cast (2) | 2010.11.24 |
[C++] 멤버 변수에서 생성자를 가진 변수 만들기 (0) | 2010.10.06 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
TAG
- API
- algorithm
- jni강좌
- Quiz
- MFC
- linux
- database
- 안드로이드
- Troubleshooting
- 프로그래밍
- Visual C++
- source
- 음악
- jni
- 리눅스
- C++
- android
- java
- winapi
- AWS
- driver
- kering
- it
- 드라이버
- Python
- gcc
- C
- Cloud
- db
- NDK
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
글 보관함