포인터는 프로그래밍 언어에서 다른 변수, 혹은 그 변수의 메모리 공간주소를 가리키는 변수를 말합니다. 포인터가 가리키는 값을 가져오는 것을 역참조라고 합니다.
int a; //메모리 공간에 4byte짜리 정수 저장하는 공간을 할당해줘! 그리고 그 공간의 이름을 a라고 하겠어!

C언어에는 메모리공간의 주소값을 다루는 DataType을 가지고 있습니다. 그 DataType을 포인터형이라고 합니다. (포인터는 어딘가를 가르킨다는 의미입니다.)
쉽게 말하면, 변수를 선언하면 메모리에 변수의 DataType만큼의 공간의 할당받습니다. 포인터형은 그 메모리 공간상에 변수의 메모리 주소값을 저장하는 DataType이라고 생각하면 됩니다.
많은 사람들이 포인터를 어려워하는 이유 중 하나가 포인터형지정자랑 포인터연산자의 구분입니다. 그래서 포인터형지정자로 사용했음에도 포인터연산자의 성질대로 해석하여 오류를 범하곤 합니다. 포인터의 이해를 위해서는 먼저 둘의 차이를 명확히 구분할 필요가 있습니다.
먼저 포인터형지정자입니다. 포인터형지정자는 포인터형변수를 만들때 앞에 붙는 *를 의미합니다. 여기서 중요한 사실은 변수를 만들때입니다. 그렇습니다. 보통 변수선언할 때 붙는 *가 포인터형 지정자입니다. 물론 함수에 들어가는 매개변수에도 해당이 되는 얘기입니다.
근데 보통이라고 말한 이유가 있습니다. 왜냐하면 포인터형지정자는 형변환할때도 사용합니다. 대표적으로 나중에 배울 malloc함수에서 많이 사용합니다.
int* b; //int*(int형 포인터)int값이 저장된 공간의 주소값
b = &a; //&a는 a변수의 주소값을 반환하는 주소연산자입니다.
int** c; //int**(int형 포인터 포인터)int값이 저장된 공간의 주소값을 저장하는 공간의 주소값
c = &b
int*** d; //포인터형 변수를 만들 때 사용하는 *(asterisk)는 포인터형지정자
d = &c;
포인터연산자는 말 그대로 연산자입니다. 그 역할은 주소값과 만나면 그 주소에 있는 공간을 의미하게 됩니다.
***d; //주소값 앞에 사용하는 *(asterisk)는 포인터연산자
***&c;
**c;
**&b;
*b;
*&a;
a;
위의 7개의 식은 모두 같은 값을 나타냅니다. 일단 연산자에 대해 깊게 이해할 필요가 있습니다.
예를 들어 a+b 라는 연산을 할 때 우리는 수학적으로 a와 b를 더한다고 생각합니다. 하지만 프로그래밍적으로 생각한다면 a에 저장되어있는 '값'과 b에 저장되어있는 'b'값을 더한다고 생각할 수 있습니다. 여기서 핵심은 값입니다. 포인터연산자도 연산자이기 때문에 값에 대한 연산이 이루어 지게 됩니다.
일단 위의 식에서 ***d부터 보겠습니다. d앞에 연산자가 붙었으므로 d의 값을 불러오게됩니다. d에는 c의 주소값 즉 &c가 들어있습니다. 따라서 ***&c와 같습니다. 근데 포인터연산자는 주소값과 만나면 그 주소에 있는 공간을 의미하게 되기 때문에 **c와 같습니다. 또 c의 값은 &b이므로 **&b와 같고 이것 또한 *b와 같습니다. 포인터연산자는 이런식으로 하나씩 풀어나가면 이해하기 쉬워집니다.
그럼 이게 정말 같은지 출력을 한 번 해보겠습니다.
예시#include <stdio.h> int main() { int a; a = 20; int* pa; pa = &a; int** ppa; ppa = &pa; int*** pppa; pppa = &ppa; printf("주소값을 출력\n"); printf("&***pppa = %p\n", &***pppa); printf("&***&ppa = %p\n", &***&ppa); printf("&**ppa = %p\n", &**ppa); printf("&**&pa = %p\n", &**&pa); printf("&*pa = %p\n", &*pa); printf("&*&a = %p\n", &*&a); printf("&a = %p\n", &a); ***pppa = 4000; printf("저장하고 있는 값을 출력\n"); printf("***pppa = %d\n", ***pppa); printf("***&ppa = %d\n", ***&ppa); printf("**ppa = %d\n", **ppa); printf("**&pa = %d\n", **&pa); printf("*pa = %d\n", *pa); printf("*&a = %d\n", *&a); printf("a = %d\n", a); return 0; }
결과주소값을 출력 &***pppa = 000000ED87DAF794 &***&ppa = 000000ED87DAF794 &**ppa = 000000ED87DAF794 &**&pa = 000000ED87DAF794 &*pa = 000000ED87DAF794 &*&a = 000000ED87DAF794 &a = 000000ED87DAF794 저장하고 있는 값을 출력 ***pppa = 4000 ***&ppa = 4000 **ppa = 4000 **&pa = 4000 *pa = 4000 *&a = 4000 a = 4000
지금까지 이해한 것을 바탕으로 직접 포인터코딩을 해보겠습니다.
코드#include <stdio.h> int main() { int a; a = 1000; int* pa = &a; int* pb = pa; *pb = 10000; printf("a = %d\n", a); return 0; }
결과a = 10000
**ppa가 b의 값을 **ppb가 a의 값을 가리키도록 해보세요.
코드#include <stdio.h> int main() { int a = 20; int b = 30; int* pa = &a; int* pb = &b; int** ppa = &pa; int** ppb = &pb; printf("**ppa = %d\n", **ppa); printf("**ppb = %d\n", **ppb); int* temp; temp = pa; pa = pb; pb = temp; printf("**ppa = %d\n", **ppa); printf("**ppb = %d\n", **ppb); return 0; }
결과**ppa = 20 **ppb = 30 **ppa = 30 **ppb = 20
**ppa가 b의 값을 **ppb가 a의 값을 가리키도록 해보세요.
코드#include <stdio.h> int main() { int a = 20; int b = 30; int* pa = &a; int* pb = &b; int** ppa = &pa; int** ppb = &pb; int** temp; printf("**ppa = %d\n", **ppa); printf("**ppb = %d\n", **ppb); temp = ppa; ppa = ppb; ppb = temp; printf("**ppa = %d\n", **ppa); printf("**ppb = %d\n", **ppb); return 0; }
결과**ppa = 20 **ppb = 30 **ppa = 30 **ppb = 20
꼭 한번씩 스스로 해보시길 바랍니다. 포인터를 이해하시는데 도움이 되실겁니다.