기본 패턴
cpp
int value = 42;
int* ptr = &value; // &: 주소 연산자 — ptr에 value의 주소 저장
std::cout << ptr; // 주소 출력 (예: 0x7ffee4a1c)
std::cout << *ptr; // 역참조 — 42 출력
*ptr = 100; // 역참조로 원본 수정 → value == 100설명
포인터란
포인터는 다른 변수의 메모리 주소를 저장하는 변수입니다. * 타입 수식자로 선언하고, &로 주소를 얻고, *로 역참조합니다.
cpp
int n = 10;
int* p = &n; // p는 n의 주소를 저장
int** pp = &p; // 포인터의 포인터도 가능nullptr — 안전한 널 포인터
cpp
int* p = nullptr; // 아무것도 가리키지 않음
if (p != nullptr) {
std::cout << *p; // 역참조 전에 항상 확인
}
// 0이나 NULL 대신 nullptr을 써야 타입 안전화살표 연산자 →
포인터를 통해 구조체나 클래스 멤버에 접근할 때 사용합니다.
cpp
struct Point { int x, y; };
Point pt{3, 7};
Point* ppt = &pt;
std::cout << ppt->x; // (*ppt).x 와 동일
ppt->y = 10;포인터 산술
배열 순회에 사용됩니다. 포인터에 정수를 더하면 타입 크기만큼 이동합니다.
cpp
int arr[]{10, 20, 30, 40};
int* p = arr; // 배열 이름은 첫 번째 요소의 포인터
std::cout << *(p + 1); // 20
std::cout << *(p + 2); // 30
for (int* it = arr; it != arr + 4; ++it) {
std::cout << *it << " ";
}const 포인터 — 네 가지 조합
cpp
int a = 1, b = 2;
int* p1 = &a; // 값도 주소도 변경 가능
const int* p2 = &a; // 값 변경 불가, 주소는 변경 가능 (pointer-to-const)
int* const p3 = &a; // 값 변경 가능, 주소는 변경 불가 (const pointer)
const int* const p4 = &a; // 값도 주소도 변경 불가
// *p2 = 5; ❌ 오류
p2 = &b; // ✅ 주소 변경 가능
// p3 = &b; ❌ 오류
*p3 = 5; // ✅ 값 변경 가능포인터 vs 참조
| 포인터 | 참조 | |
|---|---|---|
| nullptr 가능 | ✅ | ❌ (항상 유효한 객체) |
| 재할당 | ✅ 다른 변수 가리키기 가능 | ❌ 한 번 바인딩 후 고정 |
| 문법 | *p, p->m | r, r.m |
| 주 사용처 | 동적 메모리, 배열 | 함수 인수, 반환값 |
빠른 정리
| 연산 | 설명 |
|---|---|
&var | 변수의 주소 반환 |
*ptr | 포인터 역참조 (가리키는 값에 접근) |
ptr->member | 포인터를 통한 멤버 접근 |
nullptr | 빈 포인터 표현 (0, NULL 대신 사용) |
const int* p | 가리키는 값을 변경할 수 없음 |
int* const p | 포인터 자체(주소)를 변경할 수 없음 |
주의할 점
초기화하지 않은 포인터는 쓰레기 주소를 가집니다. 역참조하면 정의되지 않은 동작(UB)이 발생합니다. 선언 즉시 nullptr이나 유효한 주소로 초기화하세요.
delete 없이 new로 할당한 메모리를 포인터 변수가 가리키고 있을 때, 그 포인터를 잃어버리면 메모리 누수가 발생합니다. 현대 C++에서는 원시 포인터 대신 스마트 포인터 사용을 권장합니다.
참고 링크
1 sources