C++프로그래밍/C 와 C++ 기초실습

배열과 포인터 (차이점 및 다중 포인터) + 포인터 주의 사항

season97 2024. 9. 19. 12:44

※ C / C++을 이미 알고 있으나 개인적인 공부를 위해 포스팅 하는 글이므로

C++에 대한 구체적인 정보를 담고 있지 않습니다.


배열과 포인터의 차이점 (사람들이 같다고 착각하는 이유)

1) 포인터

int* p;

ㆍ p는 단지 그곳으로 워프하는 포탈이라 생각하자

 

ㆍ 진퉁은 쩌~~~ 멀리 어딘가 있음

 

ㆍ p는 그저 주소를 담아주는 바구니일뿐 (4or8바이트)

 

2) 배열

int arr[10];

ㆍ 진짜배기 원조 데이터가 이 안에 진짜로 있는거임. (얘 자체가 진퉁 데이터)

 

ㆍ 포인터는 그냥 4or8바이트 작은 데이터(포탈) 인 반면, 배열은 진짜 거대할 수도 있음

 

ㆍ 닭장처럼 데이터의 묶음이기 때문에 많고 거대할 수 있음

 

ㆍ 배열과 배열의 이름은 다른거다. 배열의 이름은 그저 첫번째 주소를 가리키고 있는놈이다.


 

 

그렇다면 사람들이 [배열 = 포인터] 라고 착각하는 경향이 있는데 그 이유가 뭘까?

 

⊙ [ 배열의 이름 ] 은 배열의 시작 주소를 가리키는 TYPE* 포인터 와 완벽하게 호환하기 때문이다. (변환 가능)

int* p;
int arr[10] = { 1,2,3,4,5,6,7,8 };
p = arr;

ㆍ 해당 코드를 보자.

 

ㆍ 주소를 담는 바구니 p에다 arr의 시작 주소를 넣어줬다.

 

ㆍ [ TYPE형 1차원 배열 ] 과 [ TYPE*형 포인터 ] 는 완전히 호환이 된다. (문법적으로)

cout << p[0] << endl;
cout << arr[0] << endl;
cout << p[5] << endl;
cout << arr[5] << endl;
cout << *p << endl;
cout << *arr << endl;
cout << *(p + 3) << endl;
cout << *(arr + 3) << endl;

ㆍ 같은 주소를 가리키면 같은 결과가 나온다. (포인터 연산은 자료형 크기만큼 이동)

 

※ 이런식으로 호환이 되기때문에 착각하는 사람이 있는 것 같다.

더보기

※   결론 

ㆍ 배열의 이름(첫번쨰주소) = TYPE* 포인터


 

int arr2[2][2] = { {1,2},{3,4} };

ㆍ 내부적으론 1차원 배열이랑 동일하는 것을 지난 포스팅을 통해 알고있다.

int arr2[2][2] = { {1,2},{3,4} };
int** pp = (int**)arr2; //이 코드는 오류! 메모리 접근하다 터짐

ㆍ 왜 터질까?? -> 2차원 배열이 사실 내부적으론 1차원 배열과 동일 하다는 점에서 힌드를 얻을 수 있다.

 

1)  ** pp는 첫번째 메모리 주소 를 가리키고 있다 1번 타고들어갔으니 *을 하나 벗기겠다 

**pp가 가리키는 메모리 주소

pp[ 주소1 ]  <----메모리에 pp쳐서 타고 들어감 (* 하나 벗겨짐)

 

2) 주소1 에는 [ 00000001 ] 이라는 값이 들어있었음! *을 하나 더 벗기기 위해 나온 값을 타고들어갈게!

*을 하나 더벗기기 위해..

주소2에는 [쓰레기] 가 들어가있었다. 따라서 당연히 터진다. 왜이럴까??

 

ㆍ 2차원 배열과 다중포인터는 전혀 호환되지 않는다.!

int* pp = arr2; //컴파일 오류

ㆍ 해당 코드는 들어가지 않는다.. 오류 메세지를 살펴보자

 

int(*p2)[2] = arr2; //정상 작동

ㆍ 정상적으로 잘 작동 한다.

ㆍ 문법이 좀 헷갈리는 부분이 있는 거 같다

cout << (*p2)[0] << endl;
cout << (*p2)[1] << endl;
cout << (*(p2+1))[0] << endl;
cout << (*(p2+1))[1] << endl;
cout << p2[0][0] << endl;
cout << p2[0][1] << endl;
cout << p2[1][0] << endl;
cout << p2[1][1] << endl;

ㆍ 값을 확인해봐도 정상적으로 들어간다.

 

 


 

 

▶ 사실 크래쉬가 나는건 다행이다.. 어디서 터졌는지 알 수 있기때문에

 

ㆍ 진짜 위험한 상황을 한번 보기위해 프로그램을 터트리는 코드를 한번 작성해봤다.

int* TestPointer()
{
	int a = 1;
	return &a;
}

void TestWrong(int* ptr)
{
	int a[100] = {};
	a[99] = 0xAAAAAAAA; 
	*ptr = 0x12341234;
} 

int main()
{
	int* pointer = TestPointer();
	TestWrong(pointer);
    return 0;
}

※ 텍스트로 보는 스택 프레임

int* pointer = TestPointer();

 

1) [매개변수] [RET] [지역변수] [매개변수] [RET] [지역변수a]

-> a가 함수가 끝나면서 스택프레임이 사라짐

 

2) [매개변수] [RET] [지역변수]   //a는 어딘가에 주소는 유효한 상태 하지만 스택프레임으로 인해 사라진상태

-> pointer에 넘겨주었음

TestWrong(pointer);

3) 이 상태에서 해당 함수를 호출

void TestWrong(int* ptr)
{
	int a[100] = {};
	a[99] = 0xAAAAAAAA; 
	*ptr = 0x12341234;
}

디버그로 확인한 메모리

ㆍ 디버그 모드니까 오류가 나오는데 릴리즈모드에선 오류가 나지도 않음...

ㆍ 나중에 엄청 큰 문제가 생길수도 있으니 이런 상황을 주의하자.

 

★ 함수의 생명 주기를 잘 생각해서 안에서만 유효한 값을 외부로 넘겨주는짓은 절대로 해서는 안되는 짓이니 주의하자