배열, 더 엄밀한 배열
Last edit on 2023-10-24
int a[10];
위와 같은 변수를 보았을때, 우리는 왜 이 변수를 배열이라고 생각하는 것일까?
배열(array)은 같은 타입의 변수들로 이루어진 유한 집합으로 정의됩니다. 배열을 구성하는 각각의 값을 배열 요소(element)라고 하며, 배열에서의 위치를 가리키는 숫자는 인덱스(index)라고 합니다. ...
- tcpschool.com
보통 위와 같이 int
형 16개로 이루어진 배열이 바로 a이라고 설명한다.
우리는 이 문장을 보고, 평소에 해왔던 고수준 언어들의 특징들을 조합하여,
a
는 int
형 10개로 이루어진 배열일 것이라고 추론한다.
하지만, K&R C에서는 다음과 같이 설명한다:
defines an array a of size 10, that is, a block of 10 consecutive objects named a[0], a[1], ... , a[9].
- K&R C, second edition (1988)
즉, a
가 10개의 원소를 담고있는 배열인것이 아니라, a
라는 이름의 변수가 메모리상에 연속하여 10개 존재하고, 이들을 구분짓기위해 index라고들 부르는것을 이용하여 a[i]
라고 작성하기로 약속한 것이다.
따라서, a[0], a[1], ... , a[9]
각각은 메모리상에서 연속될 뿐, 근본적으로 모두 독립된 변수다.
배열과 포인터
혹자는 배열이 포인터라고 말한다. 그렇게 생각하면 편하긴 한데, 엄밀히 따지면 다른점이 있다:
- 배열의 붕괴(decay)
- sizeof
sizeof
int a[10];
배열 a는 포인터와 같이 연산할 수 있고, 역참조 될수도 있는데, 무엇이 다르다는 걸까?
이를 이해하기 위해 배열의 크기를 재보자.
sizeof a
는 누구나 40이라고 추론할 수 있고,
실제로 그렇다.
하지만, a가 정녕 포인터였다면, 4(i386) 혹은 8(x86-64)이어야 맞지 않나?
즉, a[10]
은 a가 10개 존재한다는 정보를 보존하고 있다는 말이다.
배열의 붕괴(decay)
이러한 정보는 손실될 수 있고, 이를 배열의 붕괴(decay)라고 부른다.
int v = *(a + 1);
배열에도 포인터와 같이 정수와의 연산이 가능하다. a + 0
는 0번째 a, a + 1
은 1번째 a와 같은 식이다.
여기서 의문이 등장한다. a + 1
은 왜 1번째 a의 주소를 반환할까?
바로, a가 int*
처럼 취급되었기 때문이다. 이는 배열이 배열로써의 정보를 잃어버리고, int*
의 성질을 띄게되었음을 의미하며, 이를 배열의 붕괴(decay)라고 부르는 것이다.
&a + 1
반대로, 위의 코드는 40바이트만큼 오프셋이 증가한다. a[10]
의 주소를 가져옴으로써,
배열의 정보가 보존되었기 때문이다.