2025. 4. 9. 22:27ㆍProgramming Language/C++
포인터와 배열의 관계에 대해서 알아두기에 앞서 먼저 알아둬야할 부분에 대해서 나열해보도록 하자.
0. 배열과 포인터의 관계를 알기 이전에 알아둬야 할점
1) 배열의 이름은 배열의 첫 번째 요소의 주소
이렇게 arr을 출력했을때의 값과 &을 붙여 arr의 0번째 요소의 주소값을 확인해보면
같은 주소를 출력하는 것을 알 수 있는데 이는 배열의 이름(배열의 변수명)만을 사용할때는 배열의 가장 첫요소의 주소값, 배열의 시작지점의 주소값을 전달한다는 것이다
2) 배열과 포인터는 매우 밀접한 관계를 가지며 서로 거의 동일하게 작동하는 경우가 많음
뒤에서 배울 내용이지만 포인터와 배열은 아주 비슷하게 작동한다.
먼저 포인터에는 배열의 이름을 전달하면서 주소값을 할당하는 것이 가능한데
arr, 배열의 이름을 사용하는 경우 그것 자체가 주소값이기 때문에 & 연산자를 따로 사용하지 않더라도 주소값을 전달할 수 있고 해당 배열의 타입을 그대로 ptr에서 사용한다면 ptr에서도 이 값을 할당하는 것도 가능하다.
이렇게 ptr에 arr을 전달해서 저장했다면
배열의 경우는 이런식으로
배열변수명[index]
를 넣음으로써 원하는 위치에 있는 요소를 가져다 쓸 수 있었다.
근데 포인터에서도 동일한 기능을 하게 할 수 있는데
이렇게 사용하면
이렇게 배열의 이름을 사용했던것과 같이 원하는 요소에 값을 꺼내어 사용할 수 있다.
추가로
*(포인터변수명 + index)
와 같이 사용하면 포인터에 할당된 배열에 원하는 요소 값을 가져다 쓸 수 있게 된다.
이렇게 배열과 포인터는 매우 밀접한 관계를 가지면서 동일하게 작동하는 경우들이 많다
3) 그럼에도 불구하고 완전히 같지는 않다는 점을 알아둬야 함
따로 구술하지 않겠다만 배열과 포인터는 완전 동일하지는 않다는 점을 알아야한다
배열의 이름이 주소값을 담기 위한 값이 아니며 포인터 처럼 작은 크기로만 구성되지도 않는다는(int arr[10]의 경우는 4 * 10 의 크기인 40바이트를 가지나 int* ptr은 4바이트 혹은 8바이트임) 점을 알아둬야 한다
1. 배열의 이름과 포인터
위에서 말했다 싶이 배열의 이름은 주소값을 반환한다.
이렇게 선언된 배열에 중괄호를 포함한 아무런 인덱스를 전달하지 않고 그냥 배열의 이름만을 출력하면 배열의 첫요소의 주소값을 반환한다.
여기서 decay라는 것에 대해서 알아둘 필요가 있는데 이는 사전적 의미로는 부식, 쇠퇴, 썩음, 약해지거나 사라짐, 원래의 상태에서 단훈하거나 열화된 상태로 변한다 라는 의미를 가지고 있는데 이는 복잡하거나 완전했던것이 단순해지고 축소된 형태로 변하는 것을 의미한다.
그래서 프로그램이에서 decay는 어떤 의미를 가지냐면 전달했던 배열(추후에는 함수도 해당되지만)이 포인터로 암묵적으로 변한다라는 의미를 가지고 있다.
그래서 사실 배열의 이름이 배열의 첫 요소의 주소값을 전달한다고 했으나
이렇게 포인터에 담기는 순간 이 값은 포인터로 변해서 주소값을 전달하는, 즉
이렇게 decay되는 것이다.
이렇게 전달하는 경우도 std::cout << 이 내부적으로는 함수로 구현되어 있고 전달되는 arr 이 int*로 decay되어 오버로딩한 함수 중에 int * 포인터로 매개변수를 받는 함수를 찾아서 전달하게 되면서 이 주소값을 반환하는 것이다.
특이하게 배열의 이름을 전달해서 decay되지 않는 경우가 있는데 그 경우는 sizeof 연산자를 사용할때이다.
만약 sizeof로 전달한 arr이 decay되었다면 배열의 첫요소의 주소값을 저장한 크기인 int 형의 4바이트 혹은 8바이트를 반환해야하나
이렇게 int형 5개가 붙은 크기인 20 바이트를 전달한다.
반면 decay된 arr의 값을 저장한 ptr의 경우는
4바이트가 나오는 것을 볼 수 있다.
그러면 &연산자를 사용한 배열의 주소를 반환하면 어떨까?
보면 같은 주소값을 전달하는 것을 볼 수 있다.
아하 그러면 배열의 이름 = &배열의 이름 이겠구나
이건 틀리다.
보면 할당할 때부터 불가능한 것을 볼 수 있다.
이는 배열의 이름은 첫번째 인자의 주소값을 전달하고 이 주소값은 int형 타입을 가리키는 주소값인데 &arr은 arr이란 배열 자체의 주소값을 전달하는 것이므로(시작점을 전달하는 것이라서 값자체는 동일하나) 배열 자체의 타입인 int (*)[5]의 타입을 가지는 것이다.
그래서 사실 넣기 위해서는
int (*ptr)[5] = &arr;
이렇게 써야지만 넣어줄 수 있다.
이건 물론 추후에 다룰 내용이라 일단 배열의 이름(arr)이랑 배열의 주소(&arr)랑은 다르다는 것만 알아두고 넘어가자.
2. 배열의 주소를 가진 포인터의 사용
포인터에 배열의 주소값을 전달하면 포인터를 가지고 배열의 값을 사용할 수 있게 된다.
배열의 경우
배열의이름[index]
를 사용함으로 요소에 접근할 수 있는데 배열의 이름을 저장한 포인터 또한 이런 방식이 가능하다.
추가로 위에서 봤다 싶이
*(포인터변수명 + index)
를 통해서도 같은 기능을 할 수 있다고 했는데 이게 가능한 이유를 확인해보자면
이렇게 배열 하나를 포인터로 담았을때
이 값의 주소값을 한번 확인해보면
이렇게 나오는데 이는 각각 4의 차이를 가지는 것을 알 수 있다.
값을 +1, +2 했는데 왜 4 바이트씩 차이를 가지는 것일까 ?
이는 ptr의 타입에 비례해서 더해지는 값에 바이트 값이 추가되기 때문이다.
ptr의 경우는 int 타입 포인터이기 때문에 + 1이 될때 int만큼 뛰어 넘은 주소의 값을 반환하고 +2가 되었을때 int형 두개를 뛰어 넘은 주소값을 반환하는 것이다.
그런데 배열의 경우는 값이 연속적으로 나란히 할당된 형태의 변수라고 이전에 이야기 했다 싶이 arr[0]에서 4바이트 뒤의 주소는 arr[1]의 주소이고 또 거기서 4바이트 뒤의 주소는 arr[2]의 주소인 것이다.
그래서 결국
이렇게로
*(ptr + 0) == a[0]
*(ptr + 1) == a[1]
*(ptr + 2) == a[2]
라고 볼 수 있게 된다.
그리고 사실
이렇게 배열의 이름에 *를 붙여서 사용도 가능하다
이는 아까 말했듯이 arr 자체가 int* 으로 decay되기 때문에 가능한 일이다.
추가로 배열의 이름은 배열의 첫번째 요소의 주소값이라고 했기 때문에
이것도 가능하다.
배열과 포인터가 다른 부분이 추가로 하나 더 있는데 포인터의 경우는
이렇게 포인터 자체에 값을 추가하여 ptr이 가리키는 값이 arr[0]의 주소값이 아니라 arr[1]의 주소값을 보게 하는것에 문제는 없으나
arr, 배열의 이름에 ++을 추가해서 배열의 이름이 바라보는 주소값을 바꾸는 것은 불가능하다
'Programming Language > C++' 카테고리의 다른 글
Ch07. 포인터 - 04.동적 할당 (0) | 2025.04.10 |
---|---|
Ch07. 포인터 - 03.문자열과의 관계 (0) | 2025.04.09 |
Ch07. 포인터 - 01. 포인터의 기본 (0) | 2025.04.08 |
Ch06. 복합데이터 - 09. Range-based for (0) | 2025.04.08 |
Ch06. 복합데이터 - 08. std::array (0) | 2025.04.08 |