포인터와 주소 (Pointer and Address)
개요
C++에서 포인터(pointer)와 주소(address)는 메모리를 직접 제어하고 데이터를 효율적으로 관리하기 위한 핵심 개념입니다. 모든 변수는 메모리 상의 특정 위치에 저장되며, 이 위치를 나타내는 값이 바로 '주소'입니다. '포인터'는 이러한 메모리 주소를 값으로 가지는 특별한 변수입니다.
이 문서를 통해 변수의 주소를 얻는 방법, 포인터를 선언하고 사용하는 방법, 그리고 포인터를 통해 원본 변수의 값을 변경하는 '역참조(dereferencing)'에 대해 알아봅니다.
C++ 버전별 주요 키워드 도입 시기
- C++98/03: 포인터의 기본적인 개념(
*
,&
)이 이미 존재했습니다. - C++11
nullptr
: 0이나NULL
대신 포인터가 아무것도 가리키지 않음을 명시적으로 나타내는 키워드가 도입되었습니다.auto
: 변수 타입을 컴파일러가 자동으로 추론하게 하여,std::uint32_t*
와 같은 긴 포인터 타입을 간결하게 선언할 수 있습니다.<cstdint>
:std::uint32_t
와 같이 이식성 높은 고정 너비 정수형을 제공합니다.
내용 설명
아래 예제는 변수 my_age
를 선언하고, 포인터 pointer_to_some_data
가 이 변수의 주소를 가리키도록 한 뒤, 포인터를 이용해 my_age
의 값을 변경하는 과정을 보여줍니다.
auto my_age = std::uint32_t{30U};
- 32비트 부호 없는 정수형 변수
my_age
를 선언하고 값30
으로 초기화합니다. - 이 변수는 메모리의 특정 주소(예:
0x...F940
)에 할당됩니다.
- 32비트 부호 없는 정수형 변수
std::cout << &my_age << "\n";
- 주소 연산자(
&
)를 사용해my_age
변수가 저장된 메모리 주소를 출력합니다.
- 주소 연산자(
std::uint32_t *pointer_to_some_data = &my_age;
std::uint32_t
타입의 데이터를 가리키는 포인터 변수pointer_to_some_data
를 선언합니다.- 이 포인터에
my_age
의 주소(&my_age
)를 할당합니다. 이제 포인터는my_age
를 가리킵니다. - 포인터 변수 자체도 다른 메모리 주소(예:
0x...F6E8
)를 가집니다.
*pointer_to_some_data = 31;
- 역참조 연산자(
*
)를 사용해pointer_to_some_data
가 가리키는 메모리 주소에 접근합니다. - 해당 주소에 있는 값을
31
로 변경합니다. 이는my_age
의 값을 직접 변경하는 것과 동일한 효과를 가집니다.
- 역참조 연산자(
예제 코드
#include <cstdint>
#include <iostream>
int main()
{
// 1. 'my_age' 변수를 선언하고 메모리에 30이라는 값을 저장합니다.
// 이 변수는 고유한 메모리 주소를 갖습니다.
auto my_age = std::uint32_t{30U}; // 예: 주소 0x...F940에 값 30 저장
std::cout << "'my_age'의 메모리 주소: " << &my_age << "\n";
std::cout << "'my_age'의 초기 값: " << my_age << "\n\n";
// 2. 'my_age'의 주소를 저장할 포인터 변수를 선언합니다.
// 이 포인터 변수 자체도 별도의 메모리 주소를 갖습니다.
std::uint32_t *pointer_to_some_data = &my_age; // 예: 주소 0x...F6E8에 'my_age'의 주소(0x...F940)를 저장
std::cout << "'pointer_to_some_data' 포인터 변수 자체의 주소: " << &pointer_to_some_data << "\n";
std::cout << "'pointer_to_some_data'가 가리키는 주소: " << pointer_to_some_data << "\n\n";
// 3. 포인터를 통해 'my_age'의 값을 변경합니다 (역참조).
*pointer_to_some_data = 31;
std::cout << "포인터를 통해 변경된 'my_age'의 값: " << my_age << "\n";
return 0;
}
실행 결과
// 실행 환경에 따라 실제 주소값은 매번 달라집니다.
'my_age'의 메모리 주소: 0x16d3b7314
'my_age'의 초기 값: 30
'pointer_to_some_data' 포인터 변수 자체의 주소: 0x16d3b7308
'pointer_to_some_data'가 가리키는 주소: 0x16d3b7314
포인터를 통해 변경된 'my_age'의 값: 31
활용팁
- Null 포인터 확인: 포인터를 사용하기 전에는 항상
nullptr
인지 확인하는 습관을 들이는 것이 안전합니다.if (ptr != nullptr) { ... }
- Dangling 포인터 주의: 이미 해제된 메모리를 가리키는 '댕글링 포인터(dangling pointer)'를 역참조하면 정의되지 않은 동작(undefined behavior)이 발생하므로 매우 위험합니다.
- 스마트 포인터 활용: C++11부터 도입된 스마트 포인터(
std::unique_ptr
,std::shared_ptr
,std::weak_ptr
)를 사용하면 메모리 누수와 같은 문제를 예방하고 메모리를 자동으로 관리할 수 있어 더 안전하고 현대적인 C++ 프로그래밍이 가능합니다. - 배열과 포인터: C-스타일 배열의 이름은 배열의 첫 번째 요소에 대한 포인터처럼 동작하여, 포인터 연산을 통해 배열 요소에 접근할 수 있습니다.
'개발 > C++ (98,03,11,14,17,20,23)' 카테고리의 다른 글
Modern C++ : const reference and value semantics (98, 11) (1) | 2025.08.12 |
---|---|
Modern C++ : string vs std::string vs std::array<char, N> (98, 11, 17) (1) | 2025.08.11 |
Modern C++ : array vs std::array (98, 11) (1) | 2025.08.10 |
Modern C++ : anonymous namespace (98, 11, 17) (2) | 2025.08.09 |
Modern C++ : C-Style static function (98, 11) (1) | 2025.08.08 |
Modern C++ : static local variables (98, 11) (2) | 2025.08.07 |
Modern C++ : const vs constexpr (98, 11, 14, 17, 20) (0) | 2025.08.06 |
Modern C++ : type conversions (11, 14, 17, 20) (4) | 2025.08.05 |