포인터는 메모리의 주소값을 저장하는 변수입니다.

포인터에서는 크게 두가지의 연산자가 있습니다.
예시를 들어 설명해보겠습니다.
int x = 10; # x변수를 10으로 선언
int* p = &x; # 포인터로 x주소 선언
위 식에서 *p는 포인터로 선언을 해줍니다.(하나의 변수)
&x는 변수 x가 저장되어있는 주소값을 의미합니다.
즉, 위의 수식은 x의 주소값을 &x를 통해 불러오고, p는 주소에 저장된 값을 반환하므로 해당 주소값에 있는 10을 가져옵니다. 그러면 p = 10을 출력하게 됩니다.
그럼 만약에 int *p = &x 라고 했을때 p의 값은 무엇일까요?
→ p는 &x이므로 x가 저장되어있는 주소값이 출력되게됩니다. 예를 들면, 0x10처럼 말입니다. 메모리 어딘가에 저장해뒀기 때문에 상황에 따라 다르게 나옵니다.
그럼 어떤 배열의 값을 포인터를 활용해서 알아봅시다.
int s[3] = {10, 20, 30};
s[0] = s;
s[1] = s+1;
s[2] = s+2;
위의 s, s+1, s+2이 주소라고 할 때, s, s+1, *s+2 로 값을 로드할 수 있습니다.
하나의 예시를 더 들어 보겠습니다.
int arr[3] = {1, 2, 3};
int *p = arr;
printf("%d\n", *(p+1)); // 2 출력
OR
p++; // 다음 요소
printf("%d\n", *p); // 2
위의 식에서 *p에 배열을 선언해줬습니다. 배열이름은 사실상 첫 번째 요소의 주소를 의미합니다.
그러므로 arr == &arr[0]입니다. 그러므로 *(p+1)은 주소값 안에있는 저장된 값에 +1을 했음을 의미하므로 2번째 값인 2를 출력할 수 있습니다.
또 다른 방법은 p값에 +1을 하는 p++을 통해 다음 요소를 불러와 출력할 수 있습니다.
포인터를 통해 함수의 값을 바꿔보겠습니다.
void change(int *p) {
*p = 100;
}
int main() {
int a = 10;
change(&a);
printf("%d\n", a); // 100
}
main 함수에서 a는 10을 의미합니다. 그러나 바로 아래에 함수 change(&a) 가 있습니다.
change함수에 따르면 p는 100을 의미합니다.
근데 &a라는 주소값을 change 함수에 전달 했으니, int p = &a이되고 *p = 100이니까 최종적으로 a값이 100으로 출력됩니다.
포인터 변수는 메모리 주소를 갖는데, 이중포인터는 이러한 포인터의 변수도 메모리를 할당받는 변수이다.
즉, 다른 포인터 변수의 주소를 갖는 포인터를 말합니다.
int a = 10;
int *p = &a;
int **pp = &p;
printf("%d\n", **pp); // 10
위의 예시에서 a는 10입니다. *p는 &a로 10입니다. 그런데 이러한 메모리 주소값 p를 메모리에 할당해서 p의 주소값(&p)을 할당한 것이 이중포인터 **p입니다. 그래서 최종 결과도 10을 출력하게 됩니다.