일반 변수는 “값”을 저장하고, 포인터 변수는 “다른 변수의 주소”를 저장해요. 그래서 “그쪽을 가리킨다”고 해서 포인터(pointer)라고 부르죠.
int *p; → “정수형 변수의 주소를 저장할 포인터 p”p = &a; → “p에 a의 주소를 넣는다” = p가 a를 가리키게 해요.*p → “p가 가리키는 곳에 있는 값”을 의미해요.*는 선언할 때는 “포인터예요”라고 알려 주고, 사용할 때는 “가리키는 곳의 값”을 가져올 때 써요.
int a = 10; int *p = &a; 라고 하면 (a는 변수 이름, p는 포인터 변수 이름):
변수 a (이름이 a인 정수 변수): [ 주소 0x1000 ] → 값: 10
↑ 가리킴
포인터 변수 p: [ 주소 0x2000 ] → 값: 0x1000 (변수 a의 주소)
그래서 *p는 “p가 가리키는 곳(변수 a가 있는 0x1000 번지)에 가서 뭐가 있어? → 10”이에요.
아래 코드를 [실행]으로 바로 실행해 보세요.
a와 *p가 둘 다 10으로 나오고, &a와 p가 같은 주소로 나오면 정확히 이해한 거예요!
int *p; → 정수의 주소를 저장하는 포인터 pp = &a; → p가 a를 가리키게 함*p → p가 가리키는 곳에 있는 값int a = 10, b = 20;과 포인터 p를 선언한 뒤, 먼저 p = &a;로 *p를 출력하고, 그다음 p = &b;로 바꾼 뒤 다시 *p를 출력하세요. (10, 20이 순서대로 나와야 합니다.)
모바일에서도 이 창에서 수정·포맷·실행·복사할 수 있어요. [실행]으로 바로 결과를 볼 수 있습니다.
#include <stdio.h>
int main() {
int a = 10, b = 20;
int *p = &a;
printf("p가 a를 가리킬 때 *p: %d\n", *p);
p = &b;
printf("p가 b를 가리킬 때 *p: %d\n", *p);
return 0;
}
p는 a를, 포인터 q는 b를 가리키게 한 뒤, *p, *q, p, q를 각각 출력해 보세요.
여기서 직접 작성·실행해 보세요. [실행]으로 결과를 확인할 수 있어요.
#include <stdio.h>
int main() {
int a = 10, b = 20;
int *p = &a, *q = &b;
printf("*p = %d, *q = %d\n", *p, *q);
printf("p = %p, q = %p\n", (void*)p, (void*)q);
return 0;
}
NULL 포인터
아직 “아무것도 가리키지 않는” 상태를 나타낼 때 NULL을 넣어요. *p를 쓰기 전에 반드시 p가 유효한 주소를 가리키는지 확인하는 습관이 중요해요.
#include <stdio.h>
int main() {
int *p = NULL; /* 아무것도 가리키지 않음 */
if (p == NULL)
printf("p는 아직 아무 주소도 가리키지 않습니다.\n");
/* *p를 여기서 쓰면 안 됨! */
return 0;
}
void* 란?
void*는 “어떤 타입의 주소든 담을 수 있는” 포인터예요. 주소만 옮기고, 실제로 값을 쓸 때는 다시 올바른 타입으로 바꿔 써요.