[C] 포인터 개념, 활용방법 정리
1. 포인터의 개념
이전까지 공부한 변수는 그 자체로 자신의 자료형에 맞는 값을 저장한다. int, double, char 등이 그 예시다. 포인터는 메모리의 주소 값 자체를 저장한다. int형 변수를 만들었다고 하면, 컴퓨터 어딘가에 int가 저장된 공간이 있을 것이고, 그것의 메모리 주소를 가리키고 있는 포인터도 있다. 포인터는 메모리 주소의 값을 가지기 때문에 컴퓨터 메모리에 바로 접근할 수 있다.
int a = 5;
int 5 값을 가지는 a 변수가 있고 이것의 메모리 주소는 0xAFB03912 라고 가정해 보자.
int *b = &a;
포인터는 int *b = &a; 처럼 선언할 때 * 연산자를 사용하여 포인터 변수임을 표현한다. 이 의미는 포인터 변수 b가 a의 메모리 주소를 가리키고 있다는 의미고, b의 값은 0xAFB03912이다. 포인터 b 또한 하나의 변수기 때문에, 메모리 주소를 또 다른 0xCA23AF29로 가진다. 선언 이후에 *b라고 쓰게 되면, 여기서는 *가 간접 참조 연산자로 포인터 변수 b가 가리키는 주소의 값, 즉 여기서는 a의 값인 5가 된다.
정리하면,
주소 연산자 (&) - 변수 앞에 붙어서 변수의 메모리 '시작' 주소값을 구한다.
포인터 (*) - 포인터 변수를 선언할 때 사용한다.
간접 참조 연산자 (*) - 선언된 포인터 변수가 가리키는 변수의 값을 구한다.
이때까지 별 생각 없이 scanf(); 를 사용할 때, &a 이렇게 써서 특정한 변수가 메모리 내에 존재하는 주소의 값을 가져오고 있던 것이었다.
#include <stdio.h>
int main(void) {
int a = 5;
int *b = &a;
printf("%d\n", *b);
}
출력 결과는 5다. 포인터 변수 b가 가리키고 있는 주소 값을 잘 출력하고 있다.
2. 포인터 활용해보기
배열의 원소의 주소값 출력하기
#include <stdio.h>
int main(void) {
int a[] = {1,2,3,4,5,6,7,8,9,10};
for (int i = 0; i < 10; i++)
{
printf("%d\n", &a[i]);
}
}
결과는 다음과 같이 출력된다.
-421800320
-421800316
-421800312
-421800308
-421800304
-421800300
-421800296
-421800292
-421800288
-421800284
4씩 증가하면서 출력된다.
344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 |
... | ... | ... | 값 | 값 | 값 | 값 | ... | ... | ... |
실제로 int a = 5; 와 같이 변수를 할당하면, 메모리 주소 상에서는 위와 같이 기록된다. int형은 4 bytes를 차지하므로, 4칸을 차지한다. 배열을 이용하면 연속적인 공간에 값을 배치시킬 수 있기 때문에 위의 코드에서 배열의 메모리 주소가 4씩 증가하는 것이다.
포인터는 특정한 메모리 주소를 가리키는 변수이기 때문에, 이 변수의 메모리 주소를 가리키는 또다른 포인터를 만들 수도 있다.
#include <stdio.h>
int main(void) {
int a = 5;
int *b = &a;
int **c = &b;
printf("%d\n", **c);
}
이렇게 다중 포인터 또한 사용할 수 있다. 간혹 게임 등을 개발할 때 난독화 기법으로도 사용된다고 한다.
포인터는 컴퓨터 시스템의 특정한 메모리에 바로 접근할 수 있기 때문에, 기존에 존재하던 중요한 메모리 영역에 실수로 접근하지 않도록 해야 한다.
int *a = 0x3344af09;
*a = 0;
위와 같은 코드처럼, 특정한 포인터 변수에 어떠한 메모리 주소를 넣고 0으로 재할당해버리면, 우리는 해당 주소가 어떤 역할을 하고 있는지 모르기 때문에 우리가 컨트롤할 수 없게 될 수가 있다.
3. 배열과 포인터
배열과 포인터는 사실 동일하다. 배열을 선언한 이후에는, 배열의 이름 자체가 포인터 변수와 동일하다.
#include <stdio.h>
int main(void) {
int a[] = {1,2,3,4,5,6,7,8,9,10};
int *b = a;
printf("%d\n", b[2]);
}
변수 a에 배열을 할당하고 포인터 변수 b를 만들어서 &a가 아닌 a를 보게 만들면 3이 출력된다. 앞서 배열을 공부한 글에서 문자 배열을 입력받을 때 왜 scanf()에 &a가 아닌 a를 써야 하는지 이해되지 않았는데, 이 때문이었다.