포인터
정의: 포인터는 특정 타입의 변수의 주소를 저장할 수 있습니다. 예를 들어, int 타입의 변수의 주소를 저장하는 포인터는 다음과 같이 정의됩니다:

주소 연산자(&): 변수의 메모리 주소를 얻기 위해 주소 연산자를 사용합니다.

역참조 연산자(*): 포인터를 통해 변수의 값을 직접 접근할 수 있습니다. 이를 역참조라고 합니다.

포인터의 산술: 포인터는 증가(increment) 및 감소(decrement) 연산을 지원합니다. 배열과 연결된 포인터 산술 연산은 배열의 요소에 접근하는 데 유용합니다.
포인터와 배열: 배열의 이름은 배열의 첫 번째 요소의 주소를 나타내는 포인터로 사용될 수 있습니다.
다중 포인터: 포인터의 포인터와 같은 다중 포인터도 있습니다. 예를 들어, int **pp;는 int * 타입의 포인터를 가리키는 포인터입니다.
함수와 포인터: 함수의 인자로 포인터를 전달하여, 함수 내에서 원본 데이터를 수정할 수 있습니다. 또한 함수는 포인터를 반환할 수 있습니다.
동적 메모리 할당: malloc(), calloc(), realloc() 및 free() 함수를 사용하여 런타임에 메모리를 동적으로 할당하고 해제할 수 있습니다. 이 할당된 메모리의 주소는 포인터에 저장됩니다.

int a = &x; 는 될까?
이 코드에서는 포인터(&x)의 값을 정수 변수 a에 할당하려고 시도하고 있습니다. 이는 타입 불일치 때문에 오류가 발생합니다. 대신 int *a = &x;와 같이 a를 포인터로 선언해야 합니다.
파이썬처럼 printf(a)로 출력이 가능한가?
printf 함수는 첫 번째 인자로 포맷 문자열이 필요합니다. 메모리 주소를 출력하려면 %p 포맷 지정자를 사용해야 합니다. 그리고 a는 정수 타입이 아니므로, 주소를 출력하려면 printf("%p", a);와 같이 코드를 작성해야 합니다.
printf("%p\n", *p); 와 printf("%p\n", p); 의 차이
포인터 p는 변수 x의 주소를 가리키고 있습니다. 여기서 두 printf 문의 출력 차이에 대해 설명하겠습니다.
printf("%p\n", *p);:
이 문장에서는 포인터 p가 가리키는 위치의 값, 즉 x의 값을 출력하려고 시도하고 있습니다. 그러나 %p는 주소를 출력하는 포맷 지정자입니다. 따라서 *p가 역참조로 가져온 값 (즉, 20)을 주소로 해석하여 출력하려고 시도합니다. 이것은 의도하지 않은 동작이며, 잘못된 사용입니다.
printf("%p\n", p);:
이 문장은 p의 값, 즉 변수 x의 주소를 올바르게 출력합니다. %p 포맷 지정자는 주소를 출력하는 데 사용되므로, 이 문장은 변수 x의 메모리 주소를 올바르게 출력합니다.
이를 정리하면, printf("%p\n", *p);는 잘못된 사용이며, 원하는 변수의 주소를 출력하려면 printf("%p\n", p);를 사용해야 합니다.
변수 x의 값을 출력하려면, 다음과 같이 %d (또는 %i) 포맷 지정자를 사용하여 *p를 출력할 수 있습니다:

int x = 10;
int *p = &x;
헷갈리는 부분 : 역참조의 값(x의 값)을 x의 주소값으로 바꾸라는말 아닌가???
그렇게 해석되지 않습니다.
int *p = &x;
이 코드는 "포인터 변수 p를 선언하고, 이 포인터가 변수 x의 메모리 주소를 가리키게 하라"는 의미입니다.
여기서:
int *p: 포인터 변수 p를 선언합니다. 이 포인터는 int 타입의 변수를 가리킬 수 있습니다.
&x: 변수 x의 메모리 주소를 나타냅니다.
int *p = &x;: 포인터 변수 p를 변수 x의 주소로 초기화합니다.
역참조는 포인터 변수를 사용하여 그 포인터가 가리키는 주소의 값을 액세스할 때 발생합니다. 예를 들면, *p = 20;에서 *p는 p가 가리키는 위치, 즉 변수 x의 값을 의미하며, 이 코드는 x의 값을 20으로 변경합니다.
그럼 *p=20에서는 *p가 변수x인데 왜 int *p = &x; 여기선 *p가 변수x가 아닌건가?
여기서 키는 문맥과 문법입니다. 포인터 선언과 포인터의 역참조는 서로 다른 문맥에서 같은 기호(*)를 사용하므로 이해하기 조금 헷갈릴 수 있습니다.
포인터 선언시의 *:
int *p;
여기서 *는 포인터 변수를 선언할 때 사용됩니다. 이 문장은 "p는 정수형 변수를 가리키는 포인터다"라는 의미입니다. 이때 *p 자체가 변수 x를 의미하는 것이 아니라, 단순히 포인터 변수 p를 선언하는 것입니다.
역참조시의 *:
*p = 20;
여기서 *는 역참조를 의미합니다. 이 문장에서 *p는 "p가 가리키는 메모리 위치" 즉, 변수 x를 의미합니다. 따라서 이 문장은 p가 가리키는 변수 (여기서는 x)의 값을 20으로 변경한다는 의미입니다.
다시 int *p = &x;로 돌아가서, 이 문장은 "정수형 포인터 변수 p를 선언하고, 그것을 x의 주소로 초기화하라"는 의미입니다. 여기서 *는 포인터 변수 p를 선언하는 데 사용되며, 이는 역참조의 *와는 다른 문맥입니다.
요약하면, * 기호의 의미는 사용되는 문맥에 따라 다릅니다. 포인터 선언에서는 "포인터 변수 선언"을 의미하고, 실제 코드 실행 문맥에서는 "역참조"를 의미합니다.

여기서 p의 주소는 어떻게 구할까?
p의 주소를 구하려면 &p를 사용하면 됩니다.
p 자체는 x의 주소를 저장하고 있지만, p의 주소를 알고 싶다면 주소 연산자 &를 사용해서 p의 메모리 주소를 가져올 수 있습니다.
이를 출력하려면 다음과 같이 작성할 수 있습니다:

p는 포인터 변수이기 때문에 메모리 상의 어딘가에 저장되어 있습니다. 따라서 &p를 사용하면 해당 포인터 변수 p가 저장된 메모리의 주소를 얻을 수 있습니다.