이번에는 A Tour of C++의 1.7 Pointers, Arrays, and References를 공부하면서 이해한 내용을 정리한다. 포인터와 레퍼런스는 처음 보면 문법이 비슷해 보여 헷갈리지만, 핵심은 간단하다. 포인터(pointer)는 객체의 주소를 저장하고, 레퍼런스(reference)는 기존 객체의 다른 이름(alias)처럼 동작한다.
이번 학습에서는 단순 문법 암기보다 실제 메모리에서 어떤 일이 일어나는지를 중심으로 봤다. 특히 *p += 5, 배열에서의 p + 2, 레퍼런스가 원본 값을 변경하는 이유, 그리고 nullptr의 의미까지 연결해서 이해했다.
1.7.1 포인터(Pointer)란?
변수는 메모리 어딘가에 저장된다. 포인터는 그 변수의 값 자체가 아니라 변수가 저장된 메모리 주소를 가리키는 변수다.
int x = 10;
int* p = &x;
&x는 x의 주소(address)를 얻고, p는 그 주소를 저장한다. 따라서 p는 “x를 가리킨다(points to x)”고 표현할 수 있다.
| 표현 | 의미 |
|---|---|
x | x에 저장된 값 |
&x | x의 메모리 주소 |
p | x의 주소를 저장한 포인터 |
*p | p가 가리키는 주소에 저장된 값 |
역참조(Dereference)
int x = 10;
int* p = &x;
*p += 5;
결과적으로 x == 15가 된다. 여기서 중요한 점은 p 자체에 5를 더한 것이 아니라는 것이다. *p는 p가 가리키는 메모리의 값을 의미하므로, x가 저장된 메모리의 값을 10에서 15로 변경한 것이다.
*p += 5는 p가 포인팅하는 메모리 값에 5를 더한다.” 포인터 코드를 볼 때 주소와 그 주소에 저장된 값을 구분하는 것이 중요하다.1.7.2 포인터와 배열(Array)
C++에서 배열과 포인터는 밀접하게 연결된다. 배열 이름은 많은 표현식에서 배열의 첫 번째 원소를 가리키는 포인터처럼 사용할 수 있다.
int a[] = {10, 20, 30};
int* p = a;
이 경우 p는 첫 번째 원소 a[0]를 가리킨다.
*p // 10
*(p + 1) // 20
*(p + 2) // 30
p + 2는 메모리 주소에 단순히 숫자 2바이트를 더한다는 뜻이 아니다. 포인터의 자료형을 기준으로 두 개의 원소만큼 이동한다. p가 int*라면 p + 2는 배열의 세 번째 int 원소를 가리킨다.
{10, 20, 30}이고 p가 첫 번째 원소를 가리킨다면 *(p + 2)는 30이다.포인터를 이동해도 배열 자체가 이동하는 것은 아니다
int a[] = {10, 20, 30};
int* p = a;
p = p + 1;
이제 p는 a[1], 즉 값 20을 가리킨다. 하지만 배열 a 자체는 변하지 않는다. 변한 것은 포인터 p가 가리키는 위치다.
1.7.3 레퍼런스(Reference)란?
레퍼런스는 이미 존재하는 객체에 붙이는 다른 이름(alias)이라고 생각하면 이해하기 쉽다.
int x = 10;
int& r = x;
r = 20;
r은 x의 복사본이 아니다. x를 가리키는 별칭이므로 r = 20을 수행하면 x 역시 20이 된다.
*로 역참조하지 않아도 원본 객체를 직접 사용하는 것처럼 쓸 수 있다.포인터와 레퍼런스의 차이
| 구분 | Pointer | Reference |
|---|---|---|
| 개념 | 객체의 주소를 저장 | 기존 객체의 별칭 |
| 접근 | *p로 역참조 | 일반 변수처럼 사용 |
| 다른 객체를 가리키기 | 가능 | 초기화 후 다른 객체의 별칭으로 재지정할 수 없음 |
| 아무것도 가리키지 않는 상태 | nullptr 사용 가능 | 정상적인 reference는 객체를 참조해야 함 |
1.7.4 nullptr
포인터는 유효한 객체를 가리킬 수도 있지만, 의도적으로 아무 객체도 가리키지 않는 상태를 표현해야 할 때가 있다. 현대 C++에서는 이때 nullptr를 사용한다.
int* p = nullptr;
이 코드는 p가 현재 유효한 int 객체를 가리키지 않는다는 뜻이다.
if (p != nullptr) {
// p가 유효한 객체를 가리킬 때만 사용
std::cout << *p;
}
반대로 다음과 같은 경우를 보자.
int x = 10;
int* p = &x;
if (p != nullptr) {
// true
}
p는 x의 주소를 가지고 있으므로 nullptr가 아니다. 즉, “p는 x의 주소를 가리키고 있으므로 nullptr가 아니다.”라는 식으로 판단하면 된다.
중요한 점은 nullptr인 포인터를 역참조하면 안 된다는 것이다.
int* p = nullptr;
// *p = 10; // 잘못된 접근: 역참조하면 안 됨
1.7.5 포인터 코드를 읽는 순서
포인터 코드가 복잡해 보일 때는 다음 순서로 읽으면 훨씬 쉽다.
int x = 10;
int* p = &x;
*p += 5;
먼저 x는 값 10을 가진 객체다. 다음으로 &x를 통해 x의 주소를 얻고 p가 그 주소를 저장한다. 마지막으로 *p를 통해 그 주소의 값, 즉 x에 접근해 5를 더한다. 따라서 최종 x는 15다.
1.7.6 이번 학습에서 헷갈리기 쉬운 포인트
첫째, p와 *p는 다르다. p는 주소이고 *p는 그 주소에 있는 값이다. 둘째, p + 2는 “주소 숫자에 2를 더한다”보다 “두 원소 앞으로 이동한다”고 이해하는 편이 정확하다. 셋째, reference는 복사본이 아니라 alias다. 그래서 reference를 통한 변경은 원본에 반영된다. 넷째, nullptr는 값이 0인 일반 정수라기보다 포인터가 아무 객체도 가리키지 않는 상태를 명확하게 표현하기 위한 현대 C++의 null pointer literal이다.
1.7.7 직접 확인해 본 문제
int x = 10;
int* p = &x;
*p += 5;
// x는?
정답: 15. p가 가리키는 메모리의 값을 5 증가시켰기 때문이다.
int a[] = {10, 20, 30};
int* p = a;
// *(p + 2)는?
정답: 30. p는 a[0]을 가리키고 p + 2는 a[2]를 가리킨다.
int x = 10;
int& r = x;
r = 20;
// x는?
정답: 20. r은 x의 alias이기 때문이다.
정리
이번 1.7 학습에서 가장 중요한 것은 문법 기호 자체보다 “지금 내가 다루는 것이 값인가, 주소인가, 아니면 기존 객체의 별칭인가?”를 구부하는 것이다.
int x = 10;
int* p = &x; // pointer: x의 주소
int& r = x; // reference: x의 alias
*p = 20; // x = 20
r = 30; // x = 30
여기에 nullptr까지 이해하면 포인터의 기본적인 상태 표현도 읽을 수 있다. 이후 함수 인자, 동적 메모리, 자료구조, 드라이버 코드 등을 공부할 때 포인터와 레퍼런스는 계속 등장하기 때문에 이 차이를 확실하게 잡아두는 것이 중요하다.
This article summarizes what I learned from A Tour of C++ 1.7: Pointers, Arrays, and References. The central idea is simple: a pointer stores an address, while a reference acts as another name, or alias, for an existing object.
1.7.1 Pointers
int x = 10;
int* p = &x;
*p += 5;
p stores the address of x. *p dereferences that address and accesses the value stored there. Therefore, *p += 5 changes x from 10 to 15.
1.7.2 Pointers and Arrays
int a[] = {10, 20, 30};
int* p = a;
*(p + 2); // 30
Here p points to the first element, a[0]. Pointer arithmetic moves in units of the pointed-to type, so p + 2 points to a[2]. Moving p does not modify the array itself; it only changes which element p points to.
1.7.3 References
int x = 10;
int& r = x;
r = 20;
A reference is an alias for an existing object. Since r is another name for x, assigning 20 through r also changes x to 20. Unlike a pointer, normal reference use does not require explicit dereferencing with *.
1.7.4 nullptr
int* p = nullptr;
nullptr explicitly represents a pointer that does not point to an object. If p contains &x, then p is not null. A null pointer must not be dereferenced.
if (p != nullptr) {
std::cout << *p;
}
1.7.5 A Practical Way to Read Pointer Code
When reading pointer code, ask three questions: What object exists? What address does the pointer store? What value is accessed when the pointer is dereferenced? This makes expressions such as *p += 5 much easier to reason about.
1.7.6 Common Points of Confusion
p and *p are not the same: one is an address and the other accesses the value at that address. p + 2 means moving two elements forward according to the pointer's type. A reference is not a copy but an alias. Finally, nullptr is the modern C++ null pointer literal used to express that a pointer currently points to no object.
Summary
int x = 10;
int* p = &x; // pointer: address of x
int& r = x; // reference: alias of x
*p = 20; // x becomes 20
r = 30; // x becomes 30
The most useful question is: Am I working with a value, an address, or an alias to an existing object? Understanding that distinction provides a strong foundation for later C++ topics such as function parameters, dynamic memory, data structures, and low-level systems programming.
'C++' 카테고리의 다른 글
| C++ concepts, 템플릿 인자를 제약하는 법 (0) | 2026.09.15 |
|---|---|
| C++ 1.8 Tests 정리: 조건식, short-circuit, if 초기화, switch fall-through, vector::size() (0) | 2026.09.14 |
| C++ 기초 다시 공부하기 ② 1.5~1.6 Scope, Lifetime, const와 constexpr (0) | 2026.09.13 |
| C++ std::variant, 여러 타입 중 하나를 담는 법 (0) | 2026.09.13 |
| C++ 기초 다시 공부하기 ① 1.1~1.4.2 프로그램, 함수, 타입과 초기화 (0) | 2026.09.12 |
