포인터를 알기 위해서는 메모리 할당에 대한 이해가 필요하다.
메모리 할당에 대해 알아보자
변수의 메모리 할당
- 메모리 : byte 단위, 각 바이트에 주소 지정
- 변수 위치 : 주소 (address)
- 변수 주소 접근 : "&"
- ex) a의 주소 ( &a ) = 1036번지
배열의 메모리 할당
- a[0] 의 주소 (&a[0]) = 1031번지
- a[1] 의 주소 (&a[1]) = 1035번지
- a[2] 의 주소 (&a[2]) = 1039번지
- 배열이름 a = 전체 배열 주소 = 1031번지
이제 포인터에 대해 알아보자.
포인터
포인터는 주소를 담는 변수라 할 수 있으며 4byte를 차지한다.
포인터 변수 선언
- * 를 붙여서 선언한다.
- char ch; % 문자형 변수 선언
- char* p; char *p; %문자형 포인터 변수 선언
- char = 'A' %문자형 변수에 문자 'A' 대입
- p = &ch; %포인터 변수에 변수 ch의 주소인 '&ch'를 대입
데이터 타입 별 포인터 변수
- int* : 정수형 포인터 변수
- char* : 문자형 포인터 변수
배열을 사용하는 포인터
- 배열은 사용할 메모리의 시작 위치를 기준으로 색인 작업된 요소의 위치를 계산하여 사용
포인터 배열
- 포인터 변수가 여러개 필요할 때 이를 묶어 포인터 배열로 만들수 있음
Dangling pointer
이미 해제된 메모리를 참조하려는 포인터를 가리키는 상태를 나타낸다. 이는 프로그램의 런타임 동안 발생하는 오류 중 하나로, 메모리 관리의 실수로 인해 발생할 수 있다.
Dangling Pointer는 크게 2가지 경우로 나눌 수 있음
- Dangling References
- 이미 해제된 메모리 영역을 참조하려는 경우입니다. 이 경우, 해당 메모리는 이전에 할당되었지만 이제는 무효화된 상태입니다. - Dangling Pointers to Local Variables
- 로컬 변수가 함수를 빠져나갈 때 해당 변수의 주소를 기억하고 있는 포인터가 남아있는 경우입니다. 이로 인해 해당 로컬 변수가 스택에서 제거된 후에도 포인터를 통해 접근을 시도하면 댕글링 포인터가 발생합니다.
#include <stdio.h>
#include <stdlib.h>
int* createInt() {
int x = 42;
return &x; // 로컬 변수의 주소를 반환 (잘못된 사용)
}
int main() {
int* ptr = createInt();
// 댕글링 포인터를 사용하려고 하면 안 됨
printf("%d\n", *ptr); // 잘못된 메모리 참조
return 0;
}
위의 예시에서 createInt 함수는 로컬 변수 x의 주소를 반환한다. 하지만 x는 함수가 끝날 때 소멸되므로, main 함수에서 이 주소를 사용하는 것은 Dangling Pointer 오류를 발생시킨다.
Dangling Pointer 를 방지하기 위해서는 포인터가 참조하는 메모리가 유효한지 확인하고, 필요한 경우에만 메모리를 해제하도록 주의해야 한다. 또한 로컬 변수에 대한 포인터를 반환하는 함수를 사용할 때 주의하여야 한다.
Null Pointer
어떠한 객체나 메모리 주소를 가리키지 않는 포인터를 나타냅니다. Null 포인터는 특별한 값으로, 주로 NULL (C에서), nullptr (C++에서), 또는 0으로 표현됩니다.
Null 포인터는 프로그램에서 다양한 목적으로 사용되며, 주로 다음과 같은 상황에서 등장합니다:
- 포인터 초기화
- 포인터를 사용하기 전에 초기화되지 않은 상태로 방치하면 미정의 동작을 일으킬 수 있습니다. 따라서 초기화되지 않은 포인터를 방지하기 위해 Null로 초기화하는 것이 일반적입니다. - 메모리 할당 실패
- 동적 메모리 할당 시 실패할 경우 반환되는 값은 Null 포인터입니다. - 포인터 반환값
- 함수에서 예외적인 상황이나 에러를 나타내기 위해 Null 포인터를 반환할 수 있습니다. - 포인터 초기화를 통한 초기 상태 표현
- Null 포인터를 이용하여 포인터가 유효한 객체를 가리키지 않는 상태를 명시적으로 나타낼 수 있습니다.
Null 포인터를 사용할 때 주의점은 Null 포인터를 역참조할 때 발생하는 미정의 동작을 피해야 합니다. 즉, Null 포인터가 가리키는 메모리에 접근하려고 하면 예외를 발생시키거나 런타임 에러가 발생할 수 있습니다.