안녕하세요, katte입니다.
이번 글에서는 배열 포인터에 대해서 간단하게 정리해 보도록 하겠습니다.
배열 포인터는 비슷한 이름 탓에 포인터 배열과 혼동될 수 있지만, 전혀 다른 개념입니다.
포인터 배열은 말 그대로 배열 각각의 원소가 포인터형인 것을 의미합니다.
다음과 같이 선언하고 사용할 수 있습니다.
int a = 2, b = 3, c = 4;
int* arr[3] = { &a, &b, &c }; //arr 각각의 원소가 포인터
std::cout << *arr[0]; //arr[0]는 a를 가리키는 포인터이므로, 2가 출력된다
그럼 배열 포인터는 무엇인가 하면, 배열 포인터는 배열 그 자체를 가리키는 포인터입니다.
만약 int arr[4]라는 배열이 있으면, 이 배열의 자료형은 int [4]형입니다.
즉, arr라는 배열을 가리키는 배열 포인터는 int [4]형을 가리키는 포인터인 것입니다. 사실 그렇게 새로운 개념이 아닙니다. 그저 가리키는 대상의 자료형이 배열일 뿐이죠.
이러한 배열 포인터는 다음과 같이 선언하고 할당합니다.
int arr[4] = { 1, 2, 3, 4 };
int(*parr)[4] = &arr; //전체 배열의 주소를 저장
//parr은 배열 포인터, *parr이 배열을 의미하므로 배열의 특정 원소에 접근하려면 다음과 같이 작성함
std::cout << (*parr)[1]; //2가 출력됨
//[]가 *보다 우선순위 높으므로 괄호 빼면 안된다
그런데 이때 &arr과 arr의 차이가 무엇일까요? 둘 다 주소값이긴 한데 말이죠.
배열의 이름 arr의 경우 배열 첫번째 요소의 주소를 가지고 있습니다.
그런데 막상 &arr을 출력해보면 arr과 같은 주소가 출력되는 것을 볼 수 있습니다.
arr과 &arr은 같은 주소값을 가질 수 있으나, 엄연히 데이터형이 다릅니다.
arr은 int []형이지만 &arr은 int(*) []형입니다.
이 차이를 예제를 통해 확인해보겠습니다.
int arr[4] = { 1, 2, 3, 4 };
std::cout << arr << std::endl << &arr << std::endl; //같은 주소가 출력된다
std::cout << arr + 1 << std::endl << &arr + 1 << std::endl;
이 코드의 출력 결과는 다음과 같습니다.
먼저 2행의 출력은 예상대로 같게 나왔습니다.
그러나 3행을 보시면, arr+1과 &arr+1이 12바이트의 차이가 난다는 것을 알 수 있습니다.
arr+1은 배열에서 그 다음 요소를 가리키게 됩니다. 만약 arr가 int형 배열일 경우 arr+1의 주소값은 첫번째 요소의 주소값에서 4바이트를 더한 값이 되겠죠.
반면 &arr+1은 전체 배열의 크기만큼 더해진 주소값을 의미합니다.
즉 int [4]형 배열의 경우 &arr+1은 &arr의 주소값에서 16바이트만큼 더한 값을 의미하게 됩니다.
이는 arr와 &arr이 비록 같은 주소값을 가지고 있더라도 자료형이 다르다는 것을 보여줍니다.
'Computer > C++' 카테고리의 다른 글
[C++] rvalue 참조와 move semantics (0) | 2022.11.16 |
---|---|
[C++] 함수 포인터 정리 (0) | 2022.11.11 |
[C++] cout 관련(3) - 출력 형식 지정 (0) | 2022.11.06 |
[C++] cout 관련(2) - 출력 버퍼 비우기 (0) | 2022.11.06 |
[C++] cout 관련(1) - write, put 출력 멤버함수 (0) | 2022.11.06 |