⊙ 포인터 변수가 무엇인지 이해하고 선언하고 사용하는 방법을 안다.
⊙ 포인터 대입의 의미 차이를 이해한다.
⊙ 직접 참조와 간접 참조의 차이를 안다.
메모리는 프로그램이 실행될 때 사용하는 RAM(Random Access Memory)의 한 부분이다. 메모리는 여러 칸으로 이루어져 있고, 각 칸은 고유한 주소를 가진다. 변수는 메모리의 특정 칸에 데이터를 저장하는 이름표 역할을 한다.
int a=10;
여기 내가 이렇게 정수형 변수를 선언하면 메모리의 어느 한 칸을 차지하고 들어가서 a라는 이름표를 달고 있는 것이다. 그리고 그 안에 들어있는 값은 10이다. a는 메모리라는 아파트에서 특정 주소를 차지하고 있는 것이다.
우리는 a를 사용하고 싶을 때 그냥 a를 적는다. 그러면 a 안에 들어있는 값인 10을 사용할 수 있다. 그렇다면 a안에 들어가 있는 값만 알 수 있을뿐, a의 주소가 무엇인지는 알 수 없는걸까?
변수의 주소 접근(&)
변수의 메모리 주소가 무엇인지 알고 싶다면 변수 앞에 참조 연산자 & 를 붙이면 된다. 이렇게 하면 a의 메모리 주소를 얻어올 수 있다. &a는 a의 메모리 주소를 의미한다.
&a
포인터 변수
참조 연산자로 얻어온 메모리 주소를 저장할 수 있는 변수는 없을까? 일반적인 변수는 정수, 실수, 문자를 가지지만 "포인터"는 데이터값의 메모리 번지를 가진다. 따라서 위에 참조 연산자로 얻어낸 메모리 주소는 이렇게 저장할 수 있는 것이다.
int* ptr = &a;
포인터 변수는 데이터형 옆에 * 를 붙여 선언한다. 그리고 메모리 주소값을 값으로 받는다. 값을 받아 초기화 하는 것은 선언할 때 해도 되고 대입을 사용해서 다른 줄에 해도 상관 없다.
int area=1;
int count=5;
int* pArea=&area;
pArea=&count; // 같은 유형의 다른 포인터 할당 가능
int i=pArea; // 오류 발생 : int형과 int*는 다르기 때문
포인터에는 같은 유형의 변수 주소를 할당해야 하는 규칙도 있다. double형 변수를 int* 포인터에 대입하려고 하면 오류가 발생한다. 그렇기 때문에 같은 유형이라면 사용하던 포인터를 다른 주소에 할당해도 된다. 하지만 같은 유형이라도 포인터가 아닌 변수에 포인터를 할당하는 것은 불가능하다. int형 변수에 int* 포인터를 대입하는 것은 불가능하다는 것이다.
그 외의 권장사항들을 정리하면 아래와 같다.
- 한 행에 하나의 포인터 변수만 선언하는 것을 권장(int* p1, p2는 p1만 포인터 변수 선언 의미. int *p1, *p2로 적어야 둘다 포인터)
- 지역 포인터를 초기화하지 않은 경우에는 임의의 값이 저장(nullptr 초기화 권장)
- 포인터에는 같은 유형의 변수 주소를 할당해야 함(int형 변수는 int*형 포인터로)
- *은 데이터 형에 붙여서 쓰는 것을 권장
역참조(*)
포인터 사용에서 두 가지 핵심 연산은 다음과 같다.
- 참조(&) : 변수의 메모리 주소를 얻음
- 역참조(*) : 포인터가 가리키는 주소에 저장된 값을 얻음
그 중 참조는 이미 제일 위에서 설명했고 역참조에 대해서 알아보자. 역참조는 포인터가 가리키는 주소에 저장된 값을 얻는다고 하였다. 이왕 하는김에 여직 나온 변수, 참조, 역참조, 포인터 다 정리해보자. 이런 상황이라고 생각해보자.
int a = 10; // 변수 a 선언
int* ptr = &a; // ptr은 a의 주소를 저장
이걸 그대로 표로 나타내면 이렇게 간단하게 나타낼 수 있다.
변수 이름 | 주소 | 값 |
a | 0x100 | 10 |
ptr | 0x200 | 0x100 |
a는 주소 0x100에 10이라는 값을 가지고 있고 ptr은 a의 주소를 저장하였으니 다른 주소 0x200에서 a의 주소 0x100을 가지고 있을 것이다.
그렇다면 아래 출력은 어떻게 나오겠는가?
cout << "a의 값: " << a << endl; // 10
cout << "a의 주소: " << &a << endl; // 0x100 (a의 메모리 주소)
cout << "ptr의 값: " << ptr << endl; // 0x100 (a의 주소, ptr이 가리키는 주소)
cout << "ptr이 가리키는 값: " << *ptr << endl; // 10 (ptr이 가리키는 주소의 값)
a의 값은 그대로 10이 나올 것이고 a의 주소(&a)는 0x100을 출력할 것이다. ptr은 a의 주소값을 가지고 있는 변수이기 때문에 출력하면 a의 주소인 0x100을 출력할 것이다. 마지막으로 연참조 연산자는 ptr이 가리키는 주소에 저장된 값을 출력할 것이다. 여기서는 ptr이 가리키는 주소=a의 주소이기 때문에 결국 a의 주소에 저장된 값을 출력한다. 10을 출력 한다는 얘기다.
이렇게 역참조 연산자를 이용하여 변수의 값을 사용하는 것을 간접 참조라고 부른다. 반대로 변수를 직접 사용하는 것, 위에서 보면 a의 이름을 써서 사용하는 것을 직접 참조라고 부른다.
포인터 타입을 선언할 때 사용하는 *과 역참조의 *이 헷갈리는 경우가 많다. int* ptr에서 사용하는 *는 포인터 타입을 선언하는 것을 의미하고 *ptr은 역참조 연산을 의미 한다는 점에서 다르니 구분하여 기억하자.
포인터 대입의 의미차이
이미 다 설명한 것 같지만 그래도 헷갈릴 수 있는 나를 위해서 포인터 대입의 의미 차이도 설명해보려 한다. 무슨 얘기를 하려는 건지 좀 더 자세히 보자.
int x=5;
int y=8;
int* pX=&x;
int* pY=&y;
이렇게 변수가 선언된 상태라고 생각해보자. 내가 하려는 이야기는 pX=pY와 *pX=*pY의 차이이다.
pX와 pY는 둘다 주소를 가지고 있다 이 값 대입한다는 이야기는 pY가 가진 주소 값을 pX에 대입한다는 의미이다. 하지만 *pX와 *pY는 둘다 값이다. 주소가 아니다. 그렇기 때문에 y의 값을 x의 변수에 대입하겠다는 의미와 같은 뜻인 것이다. x=y와 같다. 대입 연산자를 사용할 때 어떤 의미를 가진 식인지 잘 생각해보고 사용해야 한다.
배운 내용 정리
- 변수는 메모리의 특정 칸에 데이터를 저장하는 이름표 역할을 한다.
- 변수의 메모리 주소가 무엇인지 알고 싶다면 변수 앞에 참조 연산자 & 를 붙이면 된다.
- "포인터"는 데이터값의 메모리 번지를 가진다.
- 포인터 사용에서 참조(&)와 역참조(*)의 두 가지 핵심 연산이 있다.
- 참조 연산자는 변수의 메모리 주소를 가져오고 역참조 연산자는 포인터가 가리키는 주소에 저장된 값을 가져온다.
- 포인터에 대입 연산자를 사용할 때 어떤 의미를 가진 식인지 잘 생각해보고 사용해야 한다.
'C++ > 게임 개발자를 위한 C++ 문법' 카테고리의 다른 글
배열 자체의 정적 선언과 요소의 개별적 동적 할당 (0) | 2024.12.26 |
---|---|
다중 포함 방지 (0) | 2024.12.26 |
동적 메모리 할당과 해제 (0) | 2024.12.24 |
함수 인자 전달 방식 (0) | 2024.12.23 |
연산 주의사항 (0) | 2024.12.23 |