포인터의 기본
2025. 3. 28. 08:43ㆍProgramming Languages/C++
6.2 포인터의 기본
6.2.1 포인터란?
포인터는 메모리 주소를 저장하는 변수입니다. 즉, 다른 변수나 데이터를 "가리키는" 변수입니다.
#include <iostream>
int main() {
int num = 42; // 일반 변수
int* ptr = # // 포인터 변수 (num의 주소를 저장)
std::cout << "num의 값: " << num << std::endl;
std::cout << "num의 주소: " << &num << std::endl;
std::cout << "ptr의 값(num의 주소): " << ptr << std::endl;
std::cout << "ptr이 가리키는 값: " << *ptr << std::endl;
return 0;
}
위 코드에서:
- int* ptr: 정수를 가리키는 포인터 변수 선언
- &num: num 변수의 주소를 반환 (주소 연산자)
- *ptr: ptr이 가리키는 주소에 있는 값을 반환 (역참조 연산자)
6.2.2 포인터 선언과 초기화
포인터는 타입* 변수명 형식으로 선언합니다:
int* p1; // 정수 포인터 (권장 스타일)
int *p2; // 정수 포인터 (C 스타일)
int * p3; // 정수 포인터 (공백 스타일)
int* p4, p5; // p4는 포인터, p5는 일반 int (!주의!)
int *p6, *p7; // p6과 p7 모두 포인터
포인터 초기화:
#include <iostream>
int main() {
// 변수 주소로 초기화
int num = 42;
int* ptr1 = #
// null 포인터로 초기화
int* ptr2 = nullptr; // C++11 이후 권장
int* ptr3 = NULL; // C 스타일 (매크로)
int* ptr4 = 0; // 0으로 초기화
// 다른 포인터 값으로 초기화
int* ptr5 = ptr1;
// 초기화되지 않은 포인터 (위험!)
int* ptr6;
// 안전한 사용 예시
if (ptr2 == nullptr) {
std::cout << "ptr2는 null 포인터입니다." << std::endl;
}
return 0;
}
6.2.3 포인터 역참조
역참조 연산자(*)를 사용하여 포인터가 가리키는 메모리 위치의 값에 접근할 수 있습니다:
#include <iostream>
int main() {
int num = 42;
int* ptr = #
// 역참조로 값 읽기
std::cout << "역참조로 값 읽기: " << *ptr << std::endl;
// 역참조로 값 변경
*ptr = 100;
std::cout << "ptr을 통해 값 변경 후: " << num << std::endl;
// 원본 변수 값 변경
num = 200;
std::cout << "원본 변수 변경 후 역참조: " << *ptr << std::endl;
return 0;
}
주의: 초기화되지 않았거나 null인 포인터를 역참조하면 프로그램이 충돌할 수 있습니다!
int* nullPtr = nullptr;
*nullPtr = 42; // 오류: 널 포인터 역참조
6.2.4. void 포인터
void* 포인터는 어떤 타입의 데이터도 가리킬 수 있는 일반 포인터입니다:
#include <iostream>
int main() {
int num = 42;
double pi = 3.14159;
char letter = 'A';
// void 포인터로 여러 타입 가리키기
void* voidPtr;
voidPtr = #
std::cout << "voidPtr이 가리키는 주소: " << voidPtr << std::endl;
voidPtr = π
std::cout << "voidPtr이 가리키는 주소: " << voidPtr << std::endl;
voidPtr = &letter;
std::cout << "voidPtr이 가리키는 주소: " << voidPtr << std::endl;
// void 포인터를 역참조하려면 명시적 타입 변환 필요
// *voidPtr = 100; // 오류: void 포인터 역참조 불가
// 올바른 사용법: 캐스팅 후 역참조
voidPtr = #
*static_cast<int*>(voidPtr) = 100;
std::cout << "num의 값: " << num << std::endl;
return 0;
}
6.2.5 포인터 연산
포인터에 정수를 더하거나 빼면 해당 타입의 크기에 맞게 주소가 변경됩니다:
#include <iostream>
int main() {
int numbers[5] = {10, 20, 30, 40, 50};
int* ptr = numbers; // 배열 이름은 첫 번째 요소의 주소
std::cout << "배열 주소: " << numbers << std::endl;
std::cout << "ptr이 가리키는 주소: " << ptr << std::endl;
// 포인터 증가
ptr++;
std::cout << "ptr++ 후 주소: " << ptr << std::endl;
std::cout << "ptr이 가리키는 값: " << *ptr << std::endl; // 20
// 포인터에 정수 더하기
ptr = numbers; // 다시 첫 번째 요소로 초기화
ptr = ptr + 2; // 두 번째 요소 건너뛰기
std::cout << "ptr + 2 후 주소: " << ptr << std::endl;
std::cout << "ptr이 가리키는 값: " << *ptr << std::endl; // 30
// 역참조와 포인터 연산 결합
ptr = numbers; // 다시 첫 번째 요소로 초기화
std::cout << "*(ptr + 3): " << *(ptr + 3) << std::endl; // 40
// 포인터 뺄셈
ptr = &numbers[4]; // 마지막 요소의 주소
ptr -= 2; // 두 요소 뒤로
std::cout << "ptr -= 2 후 가리키는 값: " << *ptr << std::endl; // 30
return 0;
}
6.2.6 포인터와 배열의 관계
C++에서 배열 이름은 배열의 첫 번째 요소를 가리키는 포인터로 취급될 수 있습니다:
#include <iostream>
int main() {
int numbers[5] = {10, 20, 30, 40, 50};
// 배열 이름을 포인터처럼 사용
std::cout << "numbers[0]: " << numbers[0] << std::endl;
std::cout << "*numbers: " << *numbers << std::endl; // 같은 결과
std::cout << "numbers[1]: " << numbers[1] << std::endl;
std::cout << "*(numbers + 1): " << *(numbers + 1) << std::endl; // 같은 결과
// 포인터 표기법으로 배열 순회
int* ptr = numbers;
for (int i = 0; i < 5; i++) {
std::cout << "Element " << i << ": " << *(ptr + i) << std::endl;
}
// 또는
ptr = numbers;
for (int i = 0; i < 5; i++) {
std::cout << "Element " << i << ": " << *ptr << std::endl;
ptr++;
}
return 0;
}
배열과 포인터의 중요한 차이점:
- 배열은 연속된 메모리 블록으로, 크기가 고정되어 있습니다.
- 배열 이름은 상수 포인터로 취급되므로 다른 주소를 가리키도록 변경할 수 없습니다 (numbers = someOtherArray; 불가능).
- sizeof(배열)은 전체 배열 크기를 반환하지만, sizeof(포인터)는 포인터 변수 자체의 크기를 반환합니다.
'Programming Languages > C++' 카테고리의 다른 글
| 참조 (0) | 2025.03.28 |
|---|---|
| 동적 메모리 할당 (0) | 2025.03.28 |
| 포인터와 참조 (0) | 2025.03.28 |
| 챕터5. 실습 문제 (0) | 2025.03.28 |
| 다형성(Polymorphism) (0) | 2025.03.28 |