전체 코드


포인터 vs 배열

포인터

  • 주소를 담는 바구니 역할을 한다.
  • 원본 데이터는 저 멀리 어딘가에 존재하고, 포인터는 그곳을 가리키는 역할을 한다.
  • 쉽게 말하면 포인터는 원본 데이터로 워프하는 포탈과 같다.
int* p;

배열

  • 진짜 데이터가 저장된 원조 저장소이다.
  • 닭장처럼 같은 타입의 데이터가 묶여있는 큰 데이터 덩어리이다.
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8 };

포인터와 배열의 관계

  • 상당히 많은 사람들이 배열 = 포인터라고 착각하는 경우가 많다.
  • 배열의 이름배열의 시작 주소값을 가리키는 TYPE* 포인터로 변환이 가능하다.
p = arr;

1차원 배열과 포인터의 호환

  • 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]

다중 포인터 vs 2차원 배열

  • int** (다중 포인터)와 2차원 배열(int[][])은 서로 호환되지 않는 타입이다.
  • 잘못된 예제:
int arr1[2][2] = { {1, 2}, {3, 4} };

// [주소1] [주소2]
// int** pp = arr1;   // 호환 불가능
int** pp = (int**)arr1;  // 형변환해도 Crash 발생

메모리 구조

  • 다중 포인터는 주소를 저장하는 바구니가 연속적이지 않기 때문에 2차원 배열과 다르게 동작한다.
  • int** pp는 이중 포인터이므로 주소를 타고 간 곳에도 주소가 있어야 하지만, 실제로 arr1에는 값이 들어가 있어 정상적으로 접근할 수 없다.

2차원 배열을 포인터로 표현하기

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;
}

메모리 오염의 원인

  1. 스택 프레임 관리 문제

    • TestPointer()에서 반환된 포인터는 더 이상 유효하지 않은 지역 변수를 가리킨다.
    • TestWrong(pointer);을 호출하면 이미 해제된 메모리 공간을 덮어쓰는 문제가 발생한다.
  2. 디버깅 모드에서 감지될 수도 있고, 릴리즈 모드에서는 정상 동작할 수도 있음

    • 디버깅 모드에서는 0xCCCCCCCC 같은 특정 값으로 채워져 있어서 오류를 감지할 수 있다.
    • 릴리즈 모드에서는 예상하지 못한 동작이 발생할 수도 있다.

profile
李家네_공부방

0개의 댓글