[C++] C++에서 사용되는 개념 11탄(포인터)

Patrick!·2023년 1월 9일
0
post-thumbnail

포인터에 대하여

포인터의 개념
포인터는 변수의 형식입니다. 개체의 주소를 메모리에 저장하고 해당 개체에 액세스하는 데 사용됩니다. 포인터를 역참조 하여 포인터가 가리키는 개체의 값을 검색할 수도 있습니다.

포인터를 이렇게 정의로 알게 되면 '이게 무슨 소리야 ....' 하겠지만

조금 이를 쉽게 풀어서 설명하자면

  • 변수 : '변수'라는 바구니에 '값'을 저장하는 개념
  • 포인터 : '*포인터'라는 바구니에 '값의 주소'를 저장하는 개념

값의 주소 => 모든 변수는 메모리에 저장이 되며 변수가 메모리에 저장된 위치를 주소값이라고 한다.

즉, 모든 변수는 '값'과 '주소값'을 가지고 있다는 것이다.

포인터를 선언하는 방법

포인터를 선언하는 방법은 기존 변수를 생성하는 방법에서 [TYPE] 앞에 *를 추가적으로 작성해주면 된다.

[변수 age]
int age = 28;

[TYPE]* 변수이름 = &[변수 age]
int* now_age = &age;

하지만 초기 선언 후, &와 함께 특정 변수의 이름을 값으로 넣어줘야 한다. (이 점이 기존 변수와의 차이점이다.)

지난 시간에 베운 전역/지역변수의 접근 제약을 벗어날 수 있는 C++의 비장의 무기에 대해 알아보자.

[전체코드]
#include <iostream>
using namespace std;

void setHp (int* hp) {
	*hp = 100;
}

int main() {
	int hp = 1;
    setHp(&hp);
    
}

위의 보기는 지난 시간에 배운 지역/전역변수의 한계에서 다룬 코드에 포인터수식(*) 과 주소수식(&)만 추가한 상황이다.

여기서 우리는 지난 시간에 void setHp() 함수안에서 int main() 안의 int hp 함수를 변경할 수 없었다.
하지만 이제는 다르다!

우선 main()에서 setHp(&hp)로 인자값을 넘겼다.
포인터로 선언된 함수의 경우, 주소값을 인자값으로 넘겨줘야 한다.

int main() {
	int hp = 10;
    setHp(&hp); <- 인자값을 주고값(&)으로 넘겨준 부분!!
}

이를 조금더 자세하게 풀어보자면 int hp = 10; 의 주소값이 '0x0fd94301' 이라고 할때,

함수의 인자값으로 보내는 값은 *hp = 0x0fd94301 이 된다.

즉, setHp(int* hp) 라는 함수에 '0x0fd94301'라는 값을 보낸 것이다.

그리고 void setHp(int hp) 에서 받은 '0x0fd94301'라는 주소값을 함수 안에서는 hp로 사용한다.

void setHp(int* hp) {
	*hp = 1000;
}

void setHp() 안에서는 *hp = 1000; 이라는 로직이 진행되는데, 이를 풀어서 0x0fd94301 = 1000; 이라고 설명할 수 있다.

즉, 0x0fd94301이라는 주소값에 있는 값을 1000으로 수정한다는 의미가 된다는 것이다.

이렇게 풀어서 설명을 하는 동안, 전역/지역변수의 한계를 뛰어넘은 과정이 진행됬다.


포인터에는 왜 type를 선언해야하는가.

분명 포인터는 주소값을 가리키는 바구니라고 배웠지만 왜 타입을 선언해야하는가에 대한 의문이 들기 마련이다. (매우 신기했다.)

처음 배웠을때는 주소값만 봤을 때, String 이지 않을까 많은 생각을 했다. (하지만 이는 아니었다.)

포인터는 주소를 담아둔 변수인데 이를 사용하는 방법에 따라 type를 선언해주어야 한다.

이게 무슨 소리인가 싶지만 그래도 예시를 통해 알아보자.

int number = 1;
int* ptr = &number;

int number = 1; 이라고 선언한 변수를 저장한 *ptr이라는 포인터가 number의 주소에 들어있는 값을 정상적으로 사용하기 위해서는 number에 담긴 값과 같은 타입으로 선언하여 사용해야 한다는 것이다.
(해당 변수에 담긴 데이터를 int로 볼 것이냐를 저장해주는 것이다.)

Rookies 선생님의 어록에서도 알 수 있듯이
"데이터를 어떻게 분석하느냐에 따라서 의미가 달라진다."

변수를 선언하고 포인터로 읽어들일 때, 1바이트 2바이트 4바이트 8바이트이냐에 따라서 의미가 완전히 달라질 수 있다는 것이다.


변수와 포인터의 타입 불일치에 대하여

변수와 포인터의 타입이 일치하지 않을 때 발생하는 문제가 있다고 한다.

다음과 같이 변수와 포인터의 타입을 일치하지 않게 선언했다.

int number = 2; (int = 4byte)

__int64_t* ptr3 = (__int64_t*)&number; (__int64_t = 64byte)

우선적으로 광대한 메모리 안에는 다양한 정보의 메모리가 자리잡고 있다.
각각의 영역에는 중요한 데이터를 저장하고 있으며, 각 영역을 침범하는 사태가 발생하면 문제가 발생하는데 포인터를 조작하는데 있어 자주 발생하는 문제가 된다.

그렇기에! 대부분의 변수와 포인터의 타입을 맞추는 경우가 일반적이다. (하지만 경우에 따라 달라질 수 있다.)

분명 int number는 4byte 영역을 메모리에 할당한다.
하지만 포인터로 받은 __int64_t* ptr3는 64byte 영역을 차지한다.

이와 근접한 부분에 유저의 재화를 관리하는 부분이 있다고 한다면 해당영역까지 침범할 수 있게 된다.

즉, *ptr3 = 0xAABBCCDDEEFF; 라는 값을 넣게 된다면 4byte에 해당하는 영역을 초과하는 사태가 발생하게 된다는 것이다.

그렇기에 변수와 포인터의 타입을 맞추어서 영역을 침범하는 일은 없도록 해야한다는 것이다.


C++의 양날의 검 Pointer에 대해 배웠다... 정말이지 처음에는 무슨 소리인가... 여지껏 배운 변수의 개념과는 조금 다른 느낌이라서 생소했었다.

하지만 이를 중요시하는 C++을 배우고 있으니 감안하고 제대로 배워야한다는 생각이 들면서 실수를 하더라도 배운 것을 실수하는 일은 없도록 하자.

profile
C++와 Unreal Engine / C#과 Unity / Katalon Studio를 통한 자동화 테스트 등을 하루하루 공부한 기록

0개의 댓글