AI/C++

[C++] 포인터, 포인터와 배열의 index

mingchin 2023. 5. 8. 21:46
728x90
반응형

포인터

메모리 주소를 가리키는 변수. 프로그래밍에서 모든 변수는 특정 메모리의 '주소'에 저장된다. 

위와 같이 자료형 뒤에 *를 붙여 선언할 수 있고, 'float*'는 "해당 포인터가 가리키는 주소에 저장된 값을 float로 바라보겠다"는 것을 의미한다. 특정 변수에 대해 &을 붙여 할당해주면, 해당 변수의 값이 아닌 변수가 저장된 주소를 포인터에 전달할 수 있다. 포인터 변수 자체는 주소를 저장하고, *를 붙이면 해당 주소에 저장된 값을 바라보았을 때 결과를 받아올 수 있다.

  • float* 라 선언했으니, pint는 크기가 4byte인 실수 자료형이구나! (x)

포인터 변수는 정수형 자료형이다. 또한 그 크기는 어떻게 선언하느냐가 아니라, 해당 소스가 실행되는 환경의 OS의 자료 처리 단위에 의해 결정된다. 운영체제가 32bit 환경이라면 4btye, 64bit 환경이라면 8byte에 해당하니, 웬만하면 8byte라고 생각해도 무방하다.

  • float*라 선언했으니, pint가 가리키는 곳에는 실수만 저장돼있구나! (x)

포인터가 바라보기로 약속한 자료형(위 예시에서는 int)과 읽어들이는 곳에 저장된 자료형(위 예시에서 float)이 다른 경우 문법 오류로 해당 과정을 막아주긴 한다. 하지만 강제로 캐스팅해보면 포인터의 원리를 이해할 수 있는데, "해당 메모리에 저장된 이진수를 포인터가 정의된 방식으로 해석"하는 것이다. 즉, 2를 float의 형태로 표현한 것을 int로 바라보았을 때 위 예시처럼 '1073741824'의 결과를 뱉어낸다. 위처럼 엉망의 프로그래밍을 할 일은 없겠지만, 포인터 자체는 '8byte 정수형', 포인터 정의 시 선언하는 자료형은 '포인터가 메모리의 이진수를 바라보는 관점'임을 이해해야 한다.

더 정확히는, 포인터는 해당 메모리 주소로부터 할당된 자료형의 크기만큼의 메모리를 읽는다. 즉 위 예시에서는 value2가 저장된 메모리의 시작주소로 이동, 4btye만큼(int)의 주소를 읽어들인다. 

포인터의 연산 단위 - 선언된 자료형에 따라

각 메모리 주소 사이에는 1byte의 공간이 존재한다. 따라서 특정 메모리 주소로부터 char, int, long 등의 자료형을 선언해 읽어들이면, 자료형의 종류에 따라 읽어들이는 메모리 주소의 간격이 달라진다.

예를 들어 시작 주소가 100번지라 가정한다면, char는 100-101번지의 값을, int는 100-104번지의 값을, long은 100-108번지의 값을 읽어들이게 될 것이다. 이 원리가 포인터의 연산에 적용되며, 위와 같이 동일한 '+1'의 연산이 포인터가 어떤 자료형을 바라보도록 선언되었냐에 따라 다른 step size를 가지게 됨을 알 수 있다.

포인터와 배열

배열은 내부적으로 포인터를 활용해 구현되었고, 아래 두 가지 특징을 가진다.

  • 배열을 선언할 때, 그 크기만큼 연속적인 메모리 공간을 확보한다. 즉, 배열에 저장되는 각각의 값들은 연속된 메모리 주소 안에 저장된다.
  • 배열을 선언한 이름은 배열의 시작 주소 값을 가진다.

배열을 선언한 뒤 해당 배열의 이름을 출력해보면, 특정 메모리 주소가 저장되어 있음을 확인할 수 있다. 해당 주소로 접근해 값을 출력해보면 0번째 index의 값이고, 한 칸 뒤 값은 당연히 1번째 index의 값이다.

해당 메모리 주소 값을 변경하면, 0번째 index의 값이 변화함을 확인할 수 있다. C++문법에 정의된 특정 index의 값을 변경하는 행위 자체가 내부적으로는 포인터 변수로 해당 메모리의 값에 접근하는 것과 동일하다.

문제 상황으로 포인터 이해하기

문법적으로 오류가 있지만 상황 이해를 위한 강제 캐스팅이 포함되어 있다.

 

# 문제 1

short MyArr[10] = {1,2,3,4,5,6,7,8,9,10};
int* pint = (int*)MyArr; # 강제 캐스팅
int answer1 = *((short*)(pint+1)); # 강제 캐스팅
int answer2 = *((short*)(pint+4)); # 강제 캐스팅
printf("answer 1: %d\n", answer1);
printf("answer 2: %d", answer2);

int 포인터의 1칸(+1)은 4byte이므로. short 포인터 입장에서는 2칸(+2 = 4byte)에 해당한다. 

따라서 pint+1이 가리키는 메모리 주소는 3의 시작 메모리 주소이고(+2),

pint+4가 가리키는 메모리 주소는 9의 시작 메모리 주소(+8)이다.

즉, answer1=3, answer2=9

# 문제2

char MyArr2[2] = {1,1};
short* pint2 = (short*)MyArr2; # 강제 캐스팅
int answer3;
answer3 = *pint2;
printf("answer3 : %d\n", answer3);

pint2 시작 메모리로부터 2byte만큼을 읽어들이도록 정의된 short 포인터이므로 MyArr2의 시작 메모리 주소를 할당하면,

char 기준 연속된 2칸의 메모리, 즉 '00010001'로 저장된 메모리를 읽어들인다. 이를 int로 해석하면, 257이므로 answer3=257

 

[참고 자료]

https://youtu.be/dnU6Rqj8DFU

728x90
반응형