본문 바로가기

Programming Languages/C++

포인터

포인터의 사용 ▶ 포인터는 C 및 C++에서 매우 중요하다. 포인터(pointer)란 다른 변수, 구조체, 객체 등을 가리키는 변수로서 메모리의 주소와 직접적으로 연관된다. 포인터 변수는 다음과 같이 자료형 명칭에 *를 사용하여 선언한다.

TypeName *ptrVar;

ptrVar는 포인터 변수로서, TypeName형의 데이터를 가리킨다. 다음은 포인터를 사용하는 간단한 예이다.

#include <iostream>
using namespace std;

int main() {
	int a = 10;
	int* ptr;

	ptr = &a; // ptr에 a의 주소를 넣음
	cout << "ptr이 가리키는 값 : " << *ptr << endl;
	*ptr = 20;
	cout << "변수 a의 값 : " << a << endl;
	return 0;
}

===== 결과 =====

ptr이 가르키는 값 : 10
변수 a의 값 : 20

 

int* ptr은 int형 포인터인 ptr를 선언한 것이다. 그럼데 이 문장만으로는 ptr이 가리키는 곳이 정의되지 않았다. 따라서 이 상테에서는 ptr를 사용하면 안 된다. 먼저 ptr이 int형 값이 저장되어 있는 메모리를 가리키도록 해야 한다. ptr = &a에서 int형 변수인 a의 주소를 ptr에 넣었다. 여기에서 & 연산자는 변수의 주소를 구한다. 즉, &a는 변수 a의 주소를 나타낸다.

포인터가 적절한 메모리 주소를 가리키게 되면 포인터를 이욯하여 그 위치의 값을 액세스할 수 있다. 포인터가 가르키는 메모리의 값을 액세스할 때에는 간접 액세스 연산자(indirection operator)인 *를 포인터 변수 앞에 붙인다. 

※주의) 부주의한 포인터 사용은 프로그램의 오류를 유발한다.

포인터가 적절한 메모리 주소를 가리키게 하는 것은 매우 중요하다. 포인터를 사용하는 프로그램에서 흔히 범하기 쉬운 오류 중 하나가 바로 포인터가 잘못된 위치를 가리킴으로써 발생하기 때문이다. 예를 들면 다음과 같이 초기화가 올바르게 이루어지지 않은 포인터를 사용할 경우 프로그램 동작 중에 오느 위치를 액세스하게 될지 알 수 없으며, 심각한 오류를 야기한다.

int f(){
 int* iPtr;
 *iPtr = 10; // 올바른 초기화가 이루어지지 않은 포인터의 사용
 .....
}

포인터가 가리키는 대상이 없을 때는 그것을 알 수 있도록 하는 값이 포인터에 저장되어 있도록 하는 것이 좋은데, 이 경우 사용할 수 있는 것이 nullptr라는 키워드이다.

구조체 및 객체의 포인터 ▶ 기본 자료형뿐만 아니라 구조체나 객체에 대한 포인터도 사용할 수 있다. 기본적으로 다른 점은 없지만 흔히 사용하는 추가적인 사항에 대하여 살펴보자. 다음은 구조체 자료형의 한 예이다.

struct C2dType { double x, y; };

C2dType형의 변수 point와 c2dType의 포인터 c2dPtr를 다음과 같이 선언하였다.

C2dType point;

C2dType *c2dPtr = &point;

c2dPtr는 point를 가리키고 있는데, c2dPtr가 가리키는 구조체의 x 및 y 항목은 각각 (*c2dPtr).x 및 (*c2dPtr).y라고 표기한다(멤버 선택 연산자 ' . '의 우선순위가 간접 액세스 연산자 ' * '보다 높기 때문에 괄호를 사용해야 하는 점에 주의). 그런데 이러한 표기를 많이 사용하므로 이를 간편하게 사용할 수 있도록 ' -> '연산자를 제공하고 있다. 이를 사용하면 (*c2dPtr).x 대신 c2dPtr -> x라고 간편하게 표기할 수 있다.

const 한정어와 포인터 ▶ 포인터에도 const 한정어를 사용할 수 있다. 이때 const 한정어를 넣는 위치에 따라 의미가 달라지는 것에 주의해야 한다. 

int a =10, b = 20;

const int *ipt = &a;

*ipt = 30; // error!

ipt = &b; // OK!

위의 문장에서 ipt는 const int * 로 정의되었는데, 이는 ipt가 const int에 대한 포인터라는 의미이다. 비록 a가 const로 지정되지는 않았지만, ipt가 상수에 대한 포인터라고 지정했으므로 *ipt의 값을 수정하는 것은 허용되지 않는다. 반면, 다음 경우는 앞에서와는 차이가 있다.

int a =10, b = 20;

int *const ipt = &a;

*ipt = 30; // OK!

ipt = &b; // error!

이 경우는 포인터 ipt에 대해 const가 지정된 것이다. 즉, ipt가 가리키는 곳을 바꿀 수 없다는 것이다. 따라서 이 프로그램에서 ipt가 b를 가리키도록 바꾸는 것은 허용되지 않는다.

포인터로 상수를 가리키게 하는 것도 주의할 필요가 있다.

int a = 10;

const int b = 20;

int *pt1 = &a; // OK .... ①

int *pt2 = &b; // 오류 .... ②

const int *pt3 = &b; // OK ....

①의 경우는 아무런 문제가 없지만 의 경우는 문제가 있다. pt2를 이용해서 상수인 b를 변화시키려 할 수도 있기 때문이다. 다라서 이러한 문장은 오류이다. 의 경우는 pt3이 int형 상수에 대한 포인터로서, pt3을 이용하여 상수인 b를 수정하지 못하므로 문제가 없다. 

'Programming Languages > C++' 카테고리의 다른 글

참조  (0) 2020.05.11
메모리 할당 및 반환  (0) 2020.05.07
배열  (0) 2020.04.30
클래스  (0) 2020.04.29
구조체  (0) 2020.04.23