Ch 07. 포인터 - 05. nullptr
1. null 포인터
null 포인터는 어떤 것도 가리키지 않는 포인터를 말한다
포인터는 원래 어떤 메모리 주소를 저장하는 변수인데 null 포인터는 의미 있는 주소가 없다는 걸 표현하기 위해 특별히 설정된 값이다.
즉 아직 유효한 대상을 할당받지 않은 상태 또는 더 이상 유효하지 않은 상태를 나타낸다.
2. null 포인터는 왜 필요할까
기존에 포인터의 경우는 값을 초기화 해주지 않으면
이렇게 초기화 되지 않은 포인터를 사용하고 있다고 에러를 띄운다.
그런데 때로는 포인터가 아직 어떤 것도 가리키지 않거나, 가리킬 수 없는 상태가 필요하다.
1) 어떤걸 가리킬지 대상을 모를때
Student* p = nullptr; // 아직 대상이 없음
// 나중에 조건에 따라 할당
if (조건) {
p = new Student;
}
포인터는 선언했으나 로직에 따라 어떤 객체를 가리킬지가 다르게 결정되기에 초기 상태를 안전하게 지정하기 위해서 nullptr이 필요하다.
2) 리턴값이 실패하거나 없음(null 리턴)
Student* findStudentById(int id) {
if (id == -1) return nullptr; // 못 찾았음
return new Student;
}
함수의 반환 값이 포인터이고 이 함수를 사용해서
Student * sptr = findStudentById(10);
이렇게 포인터에 넣어줄텐데 return을 nullptr로 하지 않은 경우 값이 반환되지 않으면 해당 포인터는 쓰레기 값이 들어가면서 에러가 발생할 가능성이 있기에 이 함수의 반환 값으로 어떤것도 받지 못했다면 그 포인터는 아무것도 가리키지 않는 값으로 설정해줘야 좀 더 안정적으로 사용이 가능하다.
또한 리턴값으로 nullptr을 쓰면, 찾았는지 여부, 유효성을 명확히 알 수 있다는 점도 사용의 이유가 된다.
3) 포인터가 해제되어 뎅글링 포인터가 될 수 있는 경우
Student* p = new Student;
delete p;
p = nullptr; // dangling pointer 방지
이렇게 포인터가 동적할당된 구조체를 가리키고 있다가 delete를 통해서 해제를 하면 p 는 쓰레기 값을 가리키고 있게 되면서 뎅글링 포인터가 될 수 있다.
그렇기에 해제되는 시점 바로 직후에 nullptr을 넣어줌으로써 잘못된 사용으로 에러를 발생하는 것을 막을 수 있고
if (p != nullptr){
...
}
이런 조건문을 통해서 내가 사용하려는 함수가 이미 해제 된 함수인지 체크해가며 잘못된 접근 자체를 방지할 수도 있게 된다.
4) 조건으로 분기해서 어떤 객체가 유효한지 파악하기 위해
위에서 사용한 잘못된 접근 방지에서 사용했던것과 같이
if (studentPtr != nullptr) {
studentPtr->study();
} else {
std::cout << "학생 정보 없음" << std::endl;
}
이렇게 nullptr이라는 값으로 조건을 맞춰 해당 포인터가 아무것도 가리키지 않는 상태라면 잘못된 접근을 우회하여 방지할 수 도 있게 된다.
3. null 포인터의 사용법
널 포인터의 경우는 그냥 간단하게 포인터에 대입하여 사용하면 된다.
이러면 컴파일을 하더라도 에러를 발생시키지 않고 널포인터의 값을 출력한다.
물론 이렇게 널포인터로 할당한 경우에는 * 역참조 연산자를 사용해서 접근해서는 안된다.
결과 또한
이렇게 이상하게 종료되는 것을 알 수 있다.