포인터에 대하여 중요하기에 따로 글쓰기
포인터는 왜 사용할까?
C++에서 포인터는 메모리 주소를 저장하고 관리하는 강력한 도구입니다. 포인터를 사용하면 데이터를 직접 읽고 수정할 수 있으며, 배열과 함수에 유연하게 접근할 수 있습니다. 하지만 포인터를 잘못 사용하면 프로그램이 비정상적으로 종료되거나 예기치 않은 동작이 발생할 수 있으므로 주의해야 합니다.
포인터를 선언할때에는 앞에 *를 붙인다 ex) int타입의 포인터를 선언할때, int *ptr
int a = 100;
int* ptr = &a;
cout << a << endl; // 100
cout << ptr << endl; // a의 주소값 출력
cout << *ptr << endl; // 100 (ptr이 가리키는 값)
쉽게 이해하기
102동 1201호에는 a가 삽니다.
101동 101호에는 a의 주소가 있습니다.
이때 101동 101호에 찾아가면 a는 없지만 a의 주소를 알아내서 a를 찾아갈 수 있습니다.
포인터의 연산
포인터는 주소를 가르키므로 주소를 이동하거나 값을 읽고 변경하는것이 가능하다.
ptr + 1은 단순히 1을 더하는것이 아니라 포인터가 가르키는 데이터타입의 크기만큼 주소를 이동한다는 의미이다.
ex) int 타입은 보통 4바이트 이므로 ptr은 현재주소에서 4바이트 이동
// 예: 포인터가 0x1000을 가리킬 때
| 0x1000 | 0x1001 | 0x1002 | 0x1003 | 0x1004 |
ptr + 1은 0x1004를 가리키게 됩니다.
그렇다면 포인터변수를 배열에 할당하면 어떻게 될까?
배열 중 가장 첫번째요소를 가르키는 포인터가 됩니다.
ex)
int arr[5] = {10, 20, 30, 40, 50};
int* ptr = arr; // arr은 첫 번째 요소의 주소를 가리킴
이때 *ptr은 10을 나타내게 될 것입니다.
이때 배열의 다음 주소들은 ptr +1입니다.
ex)
cout << *ptr << endl; // 10 (arr[0])
cout << *(ptr + 1) << endl; // 20 (arr[1])
cout << *(ptr + 2) << endl; // 30 (arr[2])
주의해야될 점
포인터의 사용은 오류를 일으킬수 있습니다.
예를들어 포인터가 null 즉, 빈 메모리를 가르킨다면 런타임오류( Segmentation Fault )가 발생합니다.
int* ptr = nullptr;
*ptr = 10; // 잘못된 참조, 프로그램 비정상 종료
그러므로 널포인터를 사용해야 할때에는 반드시 조건문을 사용해 널여부를 확인합니다.
if (ptr != nullptr) {
*ptr = 10;
}
Dangling Pointer 사용이란?
Dangling Pointer는 이미 해제된(삭제된) 메모리를 참조하는 포인터입니다. 오류가 일어나지 않을 수도 있지만 예기치 않는 동작이 발생합니다.
int* ptr = new int(10);
delete ptr; // 메모리 해제
*ptr = 20; // Dangling Pointer로 잘못된 접근
그렇다면 어떻게 해야할까?
해제한 이후에는 반드시 포인터를 다시 널로 초기화해야합니다.
ptr = nullptr;
초기화 되지않는다면 어떻게 될까?
초기화를 하지않는다면 쓰레기 값을 배출하여 의도치않는 동작을 유발합니다.
int* ptr; // 초기화되지 않음
*ptr = 10; // 잘못된 접근
그렇다면 배열을 가르키는 포인터변수가 배열의 범위를 초과하면 어떻게 될까?
배열의 범위를 초과하면 오류를 일어나므로 포인터를 이용하여 배열에 접근할때에는 범위를 벗어나지 않도록 해야합니다.
ex)
int arr[5] = {1, 2, 3, 4, 5};
int* ptr = arr;
for (int i = 0; i <= 5; i++) {
cout << *(ptr + i) << endl; // i = 5는 잘못된 접근
}
그렇다면 함수도 포인터에 넣을수 있을까?
함수의 메모리 주소를 저장하는 특수한 변수를 함수포인터라 합니다.이를 통해 함수를 변수처럼 다룰 수 있으며,
함수를 매개변수로 전달하거나 반환값으로 사용할 수 있습니다.
함수 포인터를 사용하면 특정 상황에 맞는 함수를 동적으로 선택하여 호출하거나, 함수를 데이터 구조에 저장하여 필요할 때 호출할 수 있습니다.
기본구조?
리턴 타입 (*포인터 이름)(매개변수 타입들);
ex)
#include <iostream>
// 함수 선언
int add(int a, int b) {
return a + b;
}
int main() {
// 함수 포인터 선언 및 초기화
int (*funcPtr)(int, int) = add;
// 함수 호출
int result = funcPtr(10, 20); // add(10, 20)와 동일
std::cout << "Result: " << result << std::endl;
return 0;
}
위 예시와 같이 함수포인터의 선언및 초기화(주소할당)을 할때에는 &는 생략가능합니다.
funcPtr = &add; // 함수 주소를 포인터에 할당
funcPtr = add; // & 생략 가능 (함수 이름은 함수의 주소를 가리킴)
처음에 말했듯 포인터를 사용하여 함수를 변수처럼 사용할 수 있으므로 함수의 매개변수로 전달하는 것도 가능합니다.
void executeOperation(int a, int b, int (*operation)(int, int)) {
std::cout << "Result: " << operation(a, b) << std::endl;
}
ex)
int add(int a, int b) {
return a + b;
}
executeOperation(10, 20, add); // Result: 30 출력
'언리얼(Unreal) > 엔진' 카테고리의 다른 글
24.12.07 언리얼(스마트 포인터에 대하여) (0) | 2024.12.07 |
---|---|
24.12.06 언리얼 (템플릿에 대하여) (1) | 2024.12.06 |
24.12.06 언리얼( Cpp의 상속의 개념) (0) | 2024.12.06 |
24.12.06 언리얼(Cpp 멤버변수,메소드 등) 사용법 (0) | 2024.12.06 |
24.12.05 언리얼(Cpp 기본개념중 헷갈릴만한 것만) (0) | 2024.12.05 |