본문 바로가기

Programming/C&CPP

C/C++ 포인터 기본

반응형

C/C++에서 포인터는 처음에 이해하기 어려운 개념입니다.

메모리의 어느 지점을 가리키는 포인터때문에 C/C++에 흥미를 잃고는 합니다.

사실 포인터는 하나의 타입일 뿐입니다.

C를 처음 공부할 때 쓰는 scanf()를 사용할 때는 printf()와는 다르게 변수명 앞에 &를 붙입니다.

이 &가 변수의 주소값을 나타내다는 것을 알고 있습니다.

포인터는 바로 이런 메모리의 주소를 저장하는 타입입니다.

변수는 메모리의 영역에 붙어있는 이름입니다.

수가 선언되면 다음과 같이 메모리 영역이 확보가 됩니다.

int형 변수를 선언하면 이렇게 4바이트의 메모리가 할당됩니다.

포인터의 선언은 타입명* 변수명;의 형태로 선언합니다.

포인터는 흔히 후라이팬과 손잡이로 표현이 많이 되곤 합니다.

후라이팬의 중심부는 실제 할당된 메모리로 보고 포인터는 손잡이로 보는 것이죠.

일반적인 변수는 할당 시에 초기화를 하지 않으면 쓰레기 값을 갖게 됩니다.

이 쓰레기 값을 쓴다고 해서 프로그램이 바로 종료되거나 하는 경우는 적습니다.

int n;을 선언하고 화면에 출력하면 쓰레기 값이 출력될 뿐이지 프로그램이 죽진 않습니다.

하지만 포인터는 선언시에 들어 있는 쓰레기값으로 인해 프로그램이 강제 종료될 수 있습니다.

포인터가 가리키는 어느 지점이 현재 사용중인 프로세스가 할당한 곳인지, 아닌 지 알 수 없기 때문입니다.

그래서 보통 포인터형은 int* pI = NULL의 형태로 초기화를 해줍니다.

C++11에서는 nullptr이 추가되었기 때문에 NULL 대신에 사용하면 됩니다.

nullptr에 대한 내용은 아래에서 확인이 가능합니다.

2014/12/01 - [Programming/C++11&14] - [C++11] NULL을 대체하는 nullptr

NULL 영역(Windows OS에서는 64K의 NULL 영역이 존재합니다.)을 가리키게 해서

현재 초기화가 되지 않았다는 표시를 해두는 것입니다.

사실 포인터는 4바이트(64bit 환경에서는 8바이트) 크기의 변수일 뿐입니다.

거기에 메모리의 특정 영역의 주소를 담는 것입니다.

그래서 포인터는 기존에 프로세스가 할당해 놓은 메모리나 새로 할당되는 메모리를 가리키게 합니다.

아래와 같은 형식으로 사용을 합니다.

int nIndex = 0;
int* pIndex = &nIndex;

pIndex는 주소값을 갖게 되고 nIndex의 주소값을 넣기 위해서 &nIndex를 대입하는 것입니다.

함수에서 포인터를 넘겨준다는 것은 효율성의 측면에서도 이득을 가져옵니다.

만약 구조체를 함수에 전달하려면 구조체를 통채로 넘기는 것보다는

포인터(4바이트나 8바이트 크기)를 넘겨주는 것이 유리합니다.

포인터(손잡이)를 쥐어줄테니 후라이팬에 있는 요리를 직접 요리(데이터의 수정 및 참조)해보라는 것이죠.

포인터는 일반적으로는 이런 형식으로 많이 사용됩니다.

MyStructType*pMyStruct = new MyStructType;

new 연산자를 통해서 메모리의 힙(동적 할당이 이루어지는 부분) 어딘가에 생성이 됩니다.

그리고 그 결과로 생성된 메모리의 시작 주소를 반환하는 것입니다.

그럼 이 포인터의 주소가 어딘지는 알 필요는 없지만 역참조를 통해서 해당 영역을 사용할 수가 있습니다.

역참조를 할 때는 pMyStruct->Func() 형식으로 사용합니다.

또한, (*pMyStruct).Func() 형식으로도 사용하지만 잘 사용하지는 않습니다.

일반적으로 프로그램이 예기치 않게 종료되는 경우는 정상적으로 할당되지 않은 공간을 접근할 때 입니다.

그렇기 때문에 항상 초기화할 때 NULL(C++11에서는 nullptr)을 참조하게 해놓습니다.

그리고 NULL일 경우에 대한 예외 처리를 하면 이런 상황을 많이 줄어들게 됩니다.

malloc으로 메모리를 할당할 경우 할당에 실패하면 NULL을 리턴합니다.

C++에서 new로 메모리를 할당할 경우는 예외가 발생합니다.

그렇기 때문에 메모리 할당에 실패할 경우에 대한 처리도 확실하게 해야 합니다.

그리고 메모리를 할당하면 반드시 해제를 해줘야 합니다.

new는 delete로, malloc은 free로 메모리를 해제합니다.

기본 타입 변수의 경우 *변수명은 포인터가 가리키는 곳의 정보(후라이팬)을 나타내고

변수명은 포인터 타입으로 선언된 그 자체(후라이팬 손잡이 = 포인터가 가리키는 곳의 주소)를 나타냅니다.

int A = 555; 이고 포인터 변수 int* pA = &A라고 할 때, (A의 메모리 주소가 0x12345678이라고 가정)

*pA = 555이고, pA는 0x12345678 이 되는겁니다.

C/C++의 * 연산자를 역참조 연산자라고 합니다.

포인터는 사용하기 까다롭지만 C/C++를 강력하게 만들어주는 중요한 문법입니다.

반응형