스마트 포인터로 바로 가려다가 역시 다중 포인터도 빼먹을 수 없다고 생각해 먼저 작성
다중 포인터란?
포인터를 가리키는 포인터를 두고 '다중 포인터'라고 부른다.
겨우 포인터라는 큰 장벽을 넘어왔는데, 더 커다란 무언가가 앞길을 가로막는 느낌이다.
하지만 어렵게 느낄 이유는 없다. 포인터를 잘 이해하고 넘어왔다면 헤매지 않을 것이다.
다중 포인터 선언
포인터에 「 * 」을 하나 더 붙이면 그게 다중 포인터다.
// 변수 생성
const char* ch = "BVM";
// 다중 포인터
// pp[&ch] / 8Byte
// ch[BVM의 주소] / 8Byte
// .rdata [B][V][M][\0]
const char** pp = &ch;
일반 포인터 ch를 선언하고 그 주소를 갖고있는 다중 포인터 pp를 선언했다.
일반적인 64비트 환경에서 포인터는 "무조건" 8바이트의 크기를 가진다. 다중 포인터도 포인터이므로 8바이트다.
다중 포인터의 동작
const char* 변수인 ch에 "BVM"이라는 문자열의 주소를 넘겨줬다.
해당 문자열은 .rdata 영역에 [B][V][M][\0]의 형태로 저장되어 있다. 우린 ch가 가리키는 내용을 바꿀 것이다.
어차피 포인터이기 때문에 이전에 하던 것과 비슷하게 하면 될 것 같다.
void SetStrPtr(const char* str)
{
str = "UK";
}
// 변수 생성
const char* ch = "BVM";
// 단순 출력
std::cout << ch << "\n";
// 일반 포인터
// [파라미터][RET][로컬변수(ch( BVM의 주소 )]
SetStrPtr(ch);
std::cout << ch << "\n";
포인터를 받는 함수 SetStrPtr()을 만들고 포인터가 가리키는 값을 "UK"로 수정하게 했다.
이를 실행하면 아래와 같은 결과가 나온다.
값이 "UK"로 변하지 않았다. 분명 이전에 하던 것과 다른 게 없는데 왜 바뀌지 않았을까?
먼저 ch는 const이기 때문에 *ch = "UK"; 의 형태로는 수정을 할 수 없다.
str = "UK" 는 str의 주소를 건드릴뿐인 아무런 의미가 없는 코드이다.
여기서 내용물의 수정을 위해 다중 포인터를 활용할 것이다.
void SetStrPtr(const char** str)
{
*str = "UK";
}
// 다중 포인터
// pp[&ch] / 8Byte
// ch[BVM의 주소] / 8Byte
// .rdata [B][V][M][\0]
const char** pp = &ch;
// [파라미터][RET][로컬변수(ch( BVM의 주소 )] [파라미터(str(ch의 주소))][RET][로컬변수]
std::cout << "다중 포인터 출력 \n";
SetStrPtr(pp);
std::cout << ch << "\n";
다중 포인터 pp를 선언하고 그 내용으로 ch의 주소를 넘겨줬다.
그리고 내용을 수정해줄 함수인 SetStrPtr()을 만들었다.
이를 실행하면 아래와 같다.
다중 포인터를 사용해 조작을 시도하니 의도한 대로 내용이 변경되었다.
어떻게 동작하는지 알기 위해선 주석으로 작성한 스택 프레임의 모식을 참고할 수 있다.
SetStrPtr(pp) 호출의 스택 프레임은 [파라미터(str(ch의 주소))][RET][로컬변수] 의 형태로 이루어져 있다고 할 수 있다.
함수에 인수로 pp를 줬다는 것은 &ch를 넘긴 것과 동일하다. 따라서 [파라미터(str(ch의 주소))] 형태가 나타나는 것이다.
그러면 함수로 넘어와서 이때의 스택 프레임은 이렇게 된다.
[파라미터][RET][로컬변수(ch( BVM의 주소 )]
ch의 주소를 줬다는 것은 BVM의 주소를 조작할 수 있다는 뜻이 된다.*str = "UK"; 의 동작은 「[U][K][\0]라는 새로운 데이터를 .rdata 영역에 할당하고, ch가 가리키는 주소를 새로 생성한 데이터의 주소로 변경」이라 할 수 있다.
기존의 BVM의 주소는 잊고, 새로운 주소를 받는 형태로 문자열을 수정했다.
엄밀히 말하면 수정이 아니라 바꿔치기지만 지금은 그렇게 받아들이자.
추가적으로 여기서 참조의 개념을 활용할 수 있다.참조는 내부적으로 포인터와 기능이 완전히 동일하기 때문에 다음과 같은 형태로도 사용할 수 있다.
void SetStrPtrRef(const char*& str)
{
str = "UM";
}
std::cout << "레퍼런스 다중 포인터 출력 \n";
SetStrPtrRef(ch);
std::cout << ch << "\n";
&을 통해 주소를 바로 받아서 같은 결과를 얻을 수 있다. 실행 결과는 아래와 같다.
문제없이 의도한 대로 출력됨을 알 수 있다.
이제 다음엔 정말로 스마트 포인터를 필두로 한 Modern C++에 대한 복습과 STL 등 지금 부족하다고 생각되는 부분들을 복습해 나갈 예정.
'Study > C++ & C#' 카테고리의 다른 글
[C++] STL : Vector (0) | 2023.04.18 |
---|---|
[C++] 스마트 포인터 (0) | 2023.02.04 |
[C++] 포인터 기초 (0) | 2023.01.26 |
[C#, Python] C# 라이브러리를 이용한 discord.py 봇 개발 (2/2) (0) | 2023.01.15 |
[C#, Python] C# 라이브러리를 이용한 discord.py 봇 개발 (1/2) (0) | 2023.01.15 |