포인터를 어려워하는 분들이 많은 것 같다. 물론 나도 겁먹었었다.

하지만 전혀 어렵지 않다는 것을 알게 되었고 다른 사람들에게도 포인터가 어렵지 않다는 것을 전파시키고 싶다.

 

일반적으로 변수는 정수, 실수, 문자 등 데이터 을 저장한다.

하지만 포인터 변수는 위와 같은 데이터가 저장되어 있는 메모리 주소를 저장하는 특별한 변수이다.

 

변수 이름 앞에 &를 붙이면 그 변수가 저장된 메모리의 주소이다.

#include <iostream>
using namespace std;

int main() {
	int a = 100;
	cout << &a << endl; // a의 주소 출력
	return 0;
}

위 코드를 실행하면 이런 결과가 나온다.

위 코드의 실행 결과

a가 저장된 메모리의 주소가 출력되는 것을 볼 수 있다.  이처럼 &a 라는 표현은 a의 주소라는 것이다.

잊지 말자 어떤 변수의 주소를 사용하고 싶다면

&변수이름

이렇게 쓰면 된다.

 

그런데 012FF71C와 같은 주소를 어떤 변수에 저장하고 싶다면? 그 변수의 타입은?

주소는 어떤 타입의 변수에 저장해야 하는걸까?

포인터 변수에 저장하면 된다. 포인터 변수는 무조건 4바이트이다. 왜냐하면 포인터 변수는 어떤 변수(그 변수 이름을 a라고 하자)의 주소를 담는 변수인데 a가 int 타입이건 double 타입이건 char 타입이건 메모리 주소는 32비트(4바이트)이기 때문이다. (32bit 시스템의 경우)

 

그럼 포인터 변수는 무슨 타입의 변수일까?

포인터에도 여러 타입이 있다.

int 타입 a의 주소를 저장하려면 int* 타입의 포인터 변수에 저장하면 되고

short 타입 변수인 b의 주소를 저장하려면 short* 타입의 포인터 변수에 저장하면 되고

char 타입 변수인 c의 주소를 저장하려면 char* 타입의 포인터 변수에 저장하면 된다.

 

포인터 변수도 다른 변수들과 마찬가지로 사용하기 전에 선언해야 한다.

예를 들어 a라는 int 타입 변수의 주소를 pa라는 포인터 변수에 저장하려면

int a = 100;
int* pa = &a; //포인터 변수 선언 및 a의 주소 대입

이렇게 하면 된다. 이제 pa에는 a의 주소 32비트(4바이트)가 저장되어 있다.

 

메모리 몇 번지인지 pa에 저장된 주소를 보고 싶다면 직접 cout << pa; 해 보면 된다.

pa에는 주소가 담겨 있다는 것을 알 수 있다.

그러니까 cout << &a;와 cout << pa;는 똑같이 a의 주소를 출력한다.

 

그럼 int 말고 다른 타입 변수들도 포인터 변수에 주소를 저장해 보자.

int count = 5;
short status = 2;
char letter = 'A';

int* pCount = &count;
short* pStatus = &status;
char* pLetter = &letter;

이 경우 메모리 상에는 이렇게 저장된다.

count는 int 타입이기 때문에 4칸에 걸쳐서  5가 저장되고 status는 short 타입이기 때문에 두 칸에 걸쳐서 2가 저장되고 letter은 char 타입이기 때문에 한 칸에 'A'의 아스키코드 값인 65(16진수로 41)가 저장된다. (메모리는 1바이트마다 주소가 부여되어 있기 때문에 메모리 한 칸은 1바이트라고 생각하면 된다.)

그리고 포인터 변수인 pCount는 4칸에 걸쳐서 count가 저장된 메모리의 주소를 저장하고 있고

pStatus도 4칸에 걸쳐서 status가 저장된 메모리의 주소를 저장하고 있고

pLetter도 4칸에 걸쳐서 letter가 저장된 메모리의 주소를 저장하고 있는 것을 볼 수 있다.

 

그러면 여기에서 의문이 들 것이다. 일반 변수를 선언할 때 타입을 명시하는 이유는

int 타입은 메모리 4바이트를 잡아야 하고 double 타입은 8바이트, char은 1바이트, ... 이런 식으로 타입마다 잡아야 하는 메모리의 크기가 다르니까 그러는 것이지만

포인터 변수는 어차피 다 4바이트인데 왜 선언할 때 int*, short*, char* 이런 식으로 타입을 다르게 선언하는 것일까?

어차피 다 4바이트인데?

 

그 이유는 여기에 있다.

 

포인터 변수는 간접 참조 연산자 *를 통해서 그 주소에 저장된 값을 읽어올 수 있다.

예를 들면 int*형 포인터 변수 pCount에 저장된 주소를 가지고 count에 저장된 값인 5를 읽어올 수 있다.

int count = 5;
int* pCount = &count;
cout << *pCount << endl; //5 출력

pCount에는 주소인 00B3FC00이 저장되어 있다. 그리고 그 주소 00B3FC00번지에는 5가 저장되어 있다.

*pCount는 pCount에 있는 그 주소(00B3FC00번지)에 가서 거기에 있는 값을 가져오라는 것이다. 

따라서 cout << *pCount;나 cout << count;나 똑같이 5를 출력한다.

 

정리하면

*포인터변수

는 '그 주소에 가서 거기에 저장되어 있는 값을 가져와'라는 말이다.

 

그런데 문제가 있다. *pCount라고 해서 일단 00B3FC00번지에 가긴 했는데 여기에서 몇 바이트를 읽어와야 하지?

00B3FC00번지에는 00이 저장되어 있고 그 다음 번지에는 00, 그 다음 번지에는 00, 그 다음 번지에는 05, 그 다음 번지에는 00, 그 다음 번지에는 02가 저장되어 있는데 몇 칸을 읽어와야 하는 걸까?

여기에서 포인터 변수의 타입이 필요하다. pCount는 int* 타입의 포인터 변수이다. int는 4바이트이므로 00B3FC00번지부터 4바이트 만큼의 데이터를 읽어오면 된다. 즉 00000005를 읽어오는 것이다.

 

그러니까 포인터 변수의 타입은 * 연산자를 이용해서 그 주소에 있는 값을 읽어올 때 몇 칸을 읽어올지 알기 위해서 있는 것이다. int* 타입의 포인터 변수는 4바이트를 읽어오면 되고 char* 타입의 포인터 변수는 1바이트를 읽어오면 되고 double* 타입의 포인터 변수는 8바이트를 읽어오면 된다.

 

만약 변수의 타입과 포인터 변수의 타입이 다르다면

이렇게 컴파일 에러가 난다. 따라서 반드시 변수의 타입과 포인터의 타입을 일치시켜야 한다.

 

이제 포인터의 기본을 배웠으니 예제 코드를 살펴보자.

#include <iostream>
using namespace std;

int main() {
  	int count = 5;
  	int* pCount = &count;

  	cout << "The value of count is " << count << endl;
  	cout << "The address of count is " << &count << endl;
  	cout << "The address of count is " << pCount << endl;
  	cout << "The value of count is " << *pCount << endl;

  	return 0;
}

위의 코드의 실행 결과이다.

count와 *pCount는 5를, &count와 pCount는 주소를 출력하는 것을 볼 수 있다.

 

다음 글은 여기에서 이어집니다. ↓

breakcoding.tistory.com/294

 

[C++] 포인터와 배열 (포인터 심화)

지난 번에 올린 이 글에 이어지는 내용이다. (링크↓) https://breakcoding.tistory.com/81 [C++] 포인터 포인터를 어려워하는 분들이 많은 것 같다. 물론 나도 겁먹었었다. 하지만 전혀 어렵지 않다는 것을

breakcoding.tistory.com

 

+ Recent posts