int* p;
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8 };
TYPE* 포인터로 변환이 가능하다.p = arr;
TYPE형 1차원 배열과 TYPE*형 포인터는 완전히 호환된다.p = arr;
// 배열과 포인터의 사용 예제
cout << p[0] << endl; // 1
cout << arr[0] << endl; // 1
cout << p[5] << endl; // 6
cout << arr[5] << endl; // 6
cout << *p << endl; // p[0] == arr[0]
cout << *arr << endl; // arr[0]
cout << *(p + 3) << endl; // arr[3]
cout << *(arr + 3) << endl; // arr[3]
int** (다중 포인터)와 2차원 배열(int[][])은 서로 호환되지 않는 타입이다.int arr1[2][2] = { {1, 2}, {3, 4} };
// [주소1] [주소2]
// int** pp = arr1; // 호환 불가능
int** pp = (int**)arr1; // 형변환해도 Crash 발생
int** pp는 이중 포인터이므로 주소를 타고 간 곳에도 주소가 있어야 하지만, 실제로 arr1에는 값이 들어가 있어 정상적으로 접근할 수 없다. 2차원 배열을 올바르게 포인터로 다루는 방법:
int arr2[2][2] = { {1, 2}, {3, 4} };
// 올바른 선언
int(*p2)[2] = arr2; // 2차원 배열을 가리키는 포인터
// 배열 요소 접근
cout << (*p2)[0] << endl; // 1
cout << (*p2)[1] << endl; // 2
cout << (*(p2 + 1))[0] << endl; // 3
cout << (*(p2 + 1))[1] << endl; // 4
cout << p2[0][0] << endl; // 1
cout << p2[0][1] << endl; // 2
cout << p2[1][0] << endl; // 3
cout << p2[1][1] << endl; // 4
int(*p2)[2] = arr2; → 2차원 배열을 가리키는 올바른 포인터 사용 방식이다.int& TestRef()
{
int a = 1;
return a;
}
int* TestPointer()
{
int a = 1; // 지역 변수
return &a; // 지역 변수의 주소를 반환 (위험!)
}
a는 스택에서 사라지므로, 그 주소를 반환하면 유효하지 않은 메모리 주소를 가리키게 된다.void TestWrong(int* ptr)
{
int a[100] = {}; // 지역 배열
a[99] = 0xAAAAAAAA; // 디버깅을 위해 값 설정
*ptr = 0x12341234; // 잘못된 메모리 접근
}
int main()
{
int* pointer = TestPointer(); // 위험한 포인터 사용
// pointer는 유효하지 않은 주소를 가리키고 있다.
TestWrong(pointer); // 위험한 메모리 접근 발생
return 0;
}
스택 프레임 관리 문제
TestPointer()에서 반환된 포인터는 더 이상 유효하지 않은 지역 변수를 가리킨다.TestWrong(pointer);을 호출하면 이미 해제된 메모리 공간을 덮어쓰는 문제가 발생한다.디버깅 모드에서 감지될 수도 있고, 릴리즈 모드에서는 정상 동작할 수도 있음
0xCCCCCCCC 같은 특정 값으로 채워져 있어서 오류를 감지할 수 있다.