2025. 2. 12. 18:37ㆍ프로그래밍 언어/C++
📌 5. 포인터와 참조 (Pointers and References)
C++에서 포인터(Pointer)와 참조(Reference)는 메모리 주소를 직접 다루는 기능을 제공한다.
포인터와 참조를 사용하면 효율적인 메모리 관리와 객체 제어가 가능하지만,
올바르게 사용하지 않으면 메모리 누수(Memory Leak)나 프로그램 오류가 발생할 수 있다.
C++11에서는 스마트 포인터(Smart Pointer)가 도입되어 메모리 관리를 자동화할 수 있다.
이 단원에서는 포인터와 참조를 다루며, 동적 메모리 할당 및 스마트 포인터의 활용까지 학습한다.
📌 5.1 포인터 기본 개념 (*, &, nullptr)
포인터(Pointer)는 메모리 주소를 저장하는 변수이다.
일반 변수는 값을 저장하지만, 포인터는 변수의 주소를 저장하며,
이를 통해 동적 메모리 할당과 객체 직접 제어가 가능하다.
C++에서 포인터를 이해하면 메모리 관리, 배열, 동적 객체 제어, 함수 전달 등 다양한 기능을 효과적으로 활용할 수 있다.
📌 1. 포인터 연산자
포인터를 다룰 때 사용하는 주요 연산자는 다음과 같다.
연산자 | 설명 |
* (역참조 연산자) | 포인터 변수를 선언하거나, 포인터가 가리키는 값을 참조할 때 사용 |
& (주소 연산자) | 변수의 메모리 주소를 가져올 때 사용 |
nullptr (C++11 이상) | 포인터가 아무것도 가리키지 않음을 나타내는 값 (이전에는 NULL 사용) |
💡 포인터 개념 예제
#include <iostream>
int main() {
int num = 10;
int* ptr = # // 포인터 ptr에 num의 주소 저장
std::cout << "num의 값: " << num << std::endl;
std::cout << "num의 주소: " << &num << std::endl;
std::cout << "포인터 ptr이 가리키는 주소: " << ptr << std::endl;
std::cout << "ptr을 역참조한 값: " << *ptr << std::endl;
return 0;
}
🔹 출력 결과 (주소 값은 다를 수 있음)
num의 값: 10
num의 주소: 0x7ffcbf1a
포인터 ptr이 가리키는 주소: 0x7ffcbf1a
ptr을 역참조한 값: 10
💡 설명
- int* ptr = # → ptr은 num의 주소를 저장하는 포인터.
- ptr을 출력하면 num의 메모리 주소가 나옴.
- *ptr을 출력하면 ptr이 가리키는 값(즉, num)을 가져옴.
📌 2. 포인터 변수 선언 및 초기화
포인터 변수를 선언할 때는 저장할 데이터 타입을 명시해야 한다.
(1) 포인터 선언 및 초기화
int* p1; // 정수를 가리키는 포인터
double* p2; // 실수를 가리키는 포인터
char* p3; // 문자를 가리키는 포인터
- 포인터가 가리킬 변수의 타입과 포인터의 타입이 반드시 일치해야 한다.
- 포인터 변수를 선언할 때 초기화하지 않으면 쓰레기 값을 가질 수 있으므로 주의해야 한다.
(2) 포인터 초기화 방법
int num = 42;
int* p1 = # // 변수의 주소로 초기화
int* p2 = nullptr; // 안전한 초기화 (C++11 이상)
📌 3. nullptr을 사용한 안전한 포인터 초기화
C++에서는 초기화되지 않은 포인터를 사용하면 오류가 발생할 가능성이 높다.
C++11 이전에는 NULL을 사용했으나, C++11부터 nullptr가 도입되어 더 안전한 방식으로 초기화 가능하다.
💡 예제: 안전한 포인터 초기화
#include <iostream>
int main() {
int* ptr1 = nullptr; // C++11 이상에서 안전한 초기화
int* ptr2 = NULL; // C++98 방식 (사용 가능하지만 권장되지 않음)
if (ptr1 == nullptr) {
std::cout << "ptr1은 현재 아무것도 가리키지 않습니다." << std::endl;
}
return 0;
}
🔹 출력 결과
ptr1은 현재 아무것도 가리키지 않습니다.
💡 왜 nullptr이 NULL보다 좋은가?
- nullptr은 명확한 포인터 전용 타입을 가지므로, 정수와 혼동될 위험이 없음.
- NULL은 내부적으로 0으로 정의되어 있어, 정수와 비교 시 문제가 발생할 가능성이 있음.
📌 4. 포인터를 활용한 배열 및 문자열 처리
포인터는 배열과 밀접한 관계가 있으며, 배열 요소를 처리하는 데 유용하게 사용된다.
(1) 포인터와 배열의 관계
배열의 이름 자체가 첫 번째 요소의 주소를 의미하므로,
포인터를 사용하면 배열을 보다 효율적으로 다룰 수 있다.
💡 예제: 배열과 포인터
#include <iostream>
int main() {
int arr[3] = {10, 20, 30};
int* ptr = arr; // 배열 이름은 첫 번째 요소의 주소
std::cout << "배열 요소 출력 (포인터 사용): " << std::endl;
for (int i = 0; i < 3; i++) {
std::cout << *(ptr + i) << " "; // 포인터 연산 사용
}
return 0;
}
🔹 출력 결과
배열 요소 출력 (포인터 사용):
10 20 30
💡 설명
- int* ptr = arr; → 배열 이름은 첫 번째 요소의 주소.
- *(ptr + i) → 포인터 연산을 사용하여 배열 요소 접근.
(2) 포인터를 활용한 문자열 처리
문자열(char 배열)은 포인터를 이용하면 더욱 효율적으로 관리할 수 있다.
💡 예제: 문자열과 포인터
#include <iostream>
int main() {
char str[] = "Hello";
char* ptr = str; // 첫 번째 문자의 주소 저장
std::cout << "문자열 출력 (포인터 사용): ";
while (*ptr != '\0') { // NULL 문자('\0')까지 반복
std::cout << *ptr;
ptr++; // 다음 문자로 이동
}
return 0;
}
🔹 출력 결과
문자열 출력 (포인터 사용): Hello
💡 설명
- 포인터를 이용하여 문자열을 한 글자씩 읽어가는 방식으로 처리.
- *ptr → 현재 가리키는 문자 출력.
- ptr++ → 포인터를 다음 문자로 이동.
📌 5. 정리
개념 | 설명 |
포인터 기본 개념 | 포인터는 메모리 주소를 저장하는 변수 |
포인터 연산자 | * → 포인터 선언 및 역참조, & → 주소 가져오기, nullptr → 포인터 초기화 |
포인터 초기화 | **nullptr(C++11 이상)**를 사용하여 안전하게 초기화 |
포인터와 배열 | 배열 이름은 첫 번째 요소의 주소를 의미 |
포인터와 문자열 | 포인터를 사용하면 문자열을 효율적으로 관리 가능 |