포인터의 이해

유석현(SeokHyun Yu)·2022년 7월 18일

C

목록 보기
13/26
post-thumbnail

1. 포인터란 무엇인가?

포인터는 C언어가 Low레벨 언어의 특성을 지닌다고 이야기하게 만든 장본인이다.

왜냐하면 포인터를 이용하면 메모리에 직접 접근이 가능하기 때문이다.

데이터가 메모리 공간에 저장될 때, 1바이트 메모리 공간을 단위로 하나의 주소 값이 할당되며, 주소 값도 1씩 증가한다.

C언어에서는 주소의 끝이 어딘지는 쉽게 계산이 가능하기 때문에 시작번지(주소)만을 가지고 위치를 표현한다.


int형 변수 num0x12ff76번지에 할당되어 있다고 해보자.

그런데 위의 주소 값 '0x12ff76' 역시 정수이다.

따라서 이것도 저장이 가능한 값이며, 이의 저장을 위해 마련된 변수가 바로 '포인터 변수'이다.

"포인터 변수란 메모리의 주소 값을 저장하기 위한 변수이다."

참고로 '포인터'란 '변수 형태의 포인터'와 '상수 형태의 포인터'를 어우르는 표현이다.


int num=7;
int* pnum;
pnum=#

위 코드에서 int* pnum이 바로 포인터 변수의 선언이다.

pnum변수는 여기서 int형 변수의 주소 값을 저장하는 포인터 변수가 된다.

위 코드에서 등장하는 & 연산자는 '오른쪽에 등장하는 피연산자의 주소 값을 반환하는 연산자'이다.

따라서 위의 문장에서는 & 연산의 결과로 변수 num의 주소 값이 반환되며, 이를 포인터 변수 pnum에 저장하게 된다.

위의 그림에서 보이듯이 포인터 변수 pnum에는 변수 num의 시작번지 주소 값이 저장된다.

그리고 이 상황을 다음과 같이 표현한다.

"포인터 변수 pnum이 int형 변수 num을 가리킨다"

그런데 위의 그림에서 변수 num의 크기는 4바이트인데 비해 포인터 변수 pnum의 크기는 8바이트로 묘사되어 있다.

그렇다면 포인터 변수의 크기가 8바이트란 뜻인가?

포인터 변수의 크기는 4바이트가 될 수도 있고 8바이트가 될 수도 있다.

32비트(4바이트) 시스템에서는 주소 값을 32비트로 표현하기 때문에 포인터 변수의 크기가 4바이트인 반면, 64비트(8바이트) 시스템에서는 주소 값을 64비트로 표현하기 때문에 포인터 변수의 크기가 8바이트이다.

이렇듯 포인터 변수의 크기가 주소 값의 크기와 일치해야 주소 값을 저장할 수 있기 때문에 주소 값의 크기와 포인터 변수의 크기가 동일한 것은 당연하다 할 수 있다.


2. & 연산자와 * 연산자

& 연산자는 피연산자의 주소 값을 반환하는 연산자이다.

int num=5;
int* pnum=#
double* pnum2=# //오류발생

이렇든 &연산자의 피연산자는 변수여야 하며, 상수는 피연산자가 될 수 없다.

그리고 변수의 자료형에 맞지 않는 포인터 변수의 선언은 문제가 될 수 있다.


* 연산자는 포인터 변수가 가리키는 메모리 공간에 접근할 때 사용하는 연산자이다.

int num=10;
int* pnum=#
*pnum=20;

printf("%d", *pnum);

위의 포인터 변수 pnum은 변수 num을 가리키고 있다.

따라서 *pnum이 의미하는 바는 다음과 같다.

"포인터 변수 pnum이 가리키는 메모리 공간인 변수 num에 접근을 해서.."

때문에 위의 코드 마지막 두 문장은 각각 다음과 같이 해석이 된다.

"포인터 변수 pnum이 가리키는 메모리 공간인 변수 num에 정수 20을 저장해라!"

"포인터 변수 pnum이 가리키는 메모리 공간인 변수 num에 저장된 값을 출력해라!"

이렇듯 사실상 *pnum은 포인터 변수 pnum이 가리키는 변수 num을 의미하는 것이다.


*pnum이 가리키는 메모리 공간에 접근을 해서 값을 읽어 들여야 하는데, 이때 어떻게 값을 읽어 들여야 하겠는가?

이와 관련해서 아래의 질문에 답을 해보자.

"pnum에 저장된 주소를 시작으로 몇 바이트를 읽어 들여야 하는가? 그리고 읽어 들인 데이터는 정수로 해석해야 하는가, 실수로 해석해야 하는가?"

위의 질문에 답을 하기 위해서는 pnum의 포인터 형 정보가 필요하다.

자! 그럼 pnum을 int형 포인터 변수라고 가정하고 답을 해보자.

"pnum이 int형 포인터 변수이므로 pnum에 저장된 주소를 시작으로 4바이트를 읽어 들여서 이를 정수로 해석해야 합니다."

그럼 pnum을 double형 포인터 변수라고 가정하고 답을 해보자.

"pnum이 double형 포인터 변수이므로 pnum에 저장된 주소를 시작으로 8바이트를 읽어 들여서 이를 실수로 해석해야 합니다."

이렇듯 포인터의 형은 메모리 공간을 참조하는 기준이 된다.

즉, 포인터 변수를 선언할 때에도 앞에 intdouble같은 자료형이 붙는 이유는, 포인터 변수에 저장되어 있는 주소를 가지고 메모리 공간에 접근할 때, 그 주소값을 기준으로 몇 바이트를 읽어 들여서 참조할지의 기준이 필요하기 때문이다.


포인터 변수를 선언만하고 초기화하지 않으면, 포인터 변수는 쓰레기 값으로 초기화 되어 어디를 가리킬지 모르게 된다.

따라서 포인터 변수를 우선 선언만 해놓고, 이후에 유효한 주소 값을 채워 넣을 생각이 있다면, 다음과 같이 초기화하는 것이 좋다.

int* ptr=0;

int* ptr2=NULL;

이는 아무 곳도 안가리킨다는 의미를 뜻한다.

profile
Backend Engineer

0개의 댓글