드디어 C언어의 꽃, 포인터입니다!
메모리의 주소
메모리는 우리가 데이터를 넣고 꺼내 쓰는 공간으로, 그 위치를 식별할 수 있어야 합니다. 마치 어느 서랍에 필기도구를 보관하는지 알아야 하는 것처럼 말이죠.
메모리는 0부터 시작 하여 1 바이트 단위로 1씩 증가합니다. 1 바이트보다 큰 변수는 여러 개의 주소를 가지게 됩니다. 예를 들어서 int형 변수 a가 1000번지에 할당되었다면 1000번지부터 1003번지까지 총 4 바이트가 할당됩니다. 그리고 그 4 바이트에 a의 값이 들어가게 되는 것이죠.
주소 연산자
변수가 할당된 메모리 공간의 시작 주소를 알면 그 위치부터 변수의 크기만큼 메모리를 사용할 수 있습니다. 이를 위해 변수를 이름이 아닌 주소(변수가 할당된 메모리 공간의 시작 주소)로 사용하는 방법이 있습니다. 주소 연산자 &를 사용하면 됩니다. 다음 예제를 통해 &의 사용법을 알아보도록 하겠습니다.
#include <stdio.h>
int main(void){
int a;
double b;
char c;
printf("int형 변수의 주소 : %u\n", &a);
printf("double형 변수의 주소 : %u\n", &b);
printf("char형 변수의 주소 : %u\n", &c);
return 0;
}
실행 결과
int형 변수의 주소 : 3287478748
double형 변수의 주소 : 3287478752
char형 변수의 주소 : 3287478747
변수를 선언하고 printf 함수 안에 변수 앞에 주소 연산자 &를 붙여 출력하니 각 변수의 주소가 출력이 된 것을 확인하실 수 있습니다. 주소들을 잘 살펴보시면 char형 변수 c의 주소와 int형 변수 a의 주소가 1이 차이나고, int형 변수 a의 주소와 double형 변수 b의 주소의 차이가 4가 나는 것을 확인할 수 있습니다. 이는 각 변수의 크기와 동일합니다. 이를 통해 double형 변수 b의 주소는 8바이트가 할당이 된 것을 추측할 수 있습니다.
위 코드에선 변환문자를 %u로 사용했으나 주소는 보통 16진수로 표기합니다. 그러니 %p를 사용하는 것이 좋습니다.
포인터와 간접 참조 연산자
이번에는 주소를 변수에 저장해서 사용하는 법을 알아보도록 하겠습니다. 이때 사용하는 연산자가 asterisk(*)입니다. 예제를 통해 선언법과 사용법을 살펴보도록 하겠습니다.
#include <stdio.h>
int main(void){
int a;
int *pa;
pa = &a;
*pa = 10;
printf("포인터로 a 값 출력 : %d\n", *pa);
printf("변수명으로 a 값 출력 : %d\n", a);
return 0;
}
실행 결과
포인터로 a 값 출력 : 10
변수명으로 a 값 출력 : 10
포인터 변수를 선언 할 때는 자료형 *변수명의 형식으로 작성하고 변수의 주소를 대입해 주면 됩니다. 위 코드에서는 int *pa로 선언한 뒤에 &a를 대입시켜 줬습니다. 그리고 *pa에 대입하는 것을 통해 a를 초기화 했습니다.
선언법을 살펴보면, int *pa에서 int는 포인터 변수 pa가 가리키고 있는 변수의 자료형을 뜻합니다. 즉, 포인터 변수 pa의 크기와는 상관이 없으며, pa변수가 가리키는 변수의 크기를 담은 것입니다. 시작주소만 알아서는 안 되니까요.