오랜시간 뒤적이지는 않았지만, 여튼 내가 내린 결론은
“& 연산자가 반환하는 주소는 형태가 좀 지랄같다.”
이다. 정확하게는
“& 연산자는, 피연산자를 하나의 원소로 갖는 가상의 배열에서, 피연산자가 위치하는 부분의 시작주소”
를 반환한다. 아ㅅㅂ 거 조낸 복잡하네.
예를 들어서…
int data;
&data; // 형식이 뭔데?
를 생각해 보자.
data는 그냥 int형이 되겠다. 따라서 &data는
{ int, int, int, …, int, int data, int, …, int, int, int }
라는 가상의 배열에서 data의 위치이다.
자, 이런 배열을 만들려면 뭘 써야 하는가? 당연히 int * 지.
이제 좀 더 가보자.
int a[3]; // 이번엔 배열이다.
&a[0]; // 이건 뭐야…
a; // 이건 왠지 알거같아…
&a; // 이건 또 뭐고.
자 우선. &a[0]. 연산자 우선순위상 [] 가 먼저 연산되니까, &의 피연산자는 int형이다.
또 쓰기 귀찮다. 결국 위에서 본 data와 경우가 같다. 형태는 int *.
다음은 a. 이건 수업시간에 들은 것처럼 첫 번째 원소의 주소, 그러니까 &a[0] 인 것은 확실하다…
그런데 뭔가 미묘하게 다르다. 이놈의 자료형은 int [3] 이다. 응?
이 말은 무슨 말이냐면, “int *와 비슷하긴 한데[footnote]실제로는 int * const 에 해당한다. 배열 주소가 바뀔 수는 없으니까…[/footnote], 길이가 3으로 명백해!” 라는 의미이다. 아ㅅㅂ 머리아프다.
여하튼 여기에서 체크해 두어야 할 것은, 배열도 결국에는 유도 자료형이라는 말이다.
int a[3] 은 “int형 3개를 원소로 갖는 배열 a를 만들어!” 가 아니고, “int형 3개를 이어붙인 배열구조를 갖는 변수 a를 만들어!” 라는 의미다.
아아… C 배운지 3년찍고 4년만에 겨우 이해했다. 변수가 배열이 아니라, 배열 형태를 갖는 변수다. ㅅㅂ 무슨소리래.
어쨌든 마지막, &a. &의 피연산자 형태는? int [3]인 배열이다. 그렇다면 다음과 같이 되겠다.
{ {int, int, int}, {int, int, int}, …, {int, int, int}, {a[0], a[1], a[2]}, {int, int, int}, …, {int, int, int}, {int, int, int} }
라는 배열에서 a 배열의 위치란 말이지. 반환형은? “int 3개를 이어붙인 구조의 배열” 형태의 변수를 가리키는 포인터, 즉 int (*)[3] 이다.
아ㅅㅂ 복잡하다… 이건 또 뭐여… 하겠지만, 그냥 납득해라. 글쓰는 나도 뭔소린지 제대로 모르겠다.
일단은, int 3개짜리 배열을 통짜로 가리키는 포인터라고만 알아두면 되겠다. 이 말을 이해했다면 이 글을 읽을 필요도 없는거고…
참고로, 포인터를 그 맴버로 갖는 배열과, 배열 자체를 가리키는 포인터 하나를 구분하기 위하여 다음과 같은 방법을 사용한다.
포인터를 맴버로 갖는 배열 : int *p[n] : int형 포인터 n개 모인 새로운 자료형을 갖는 변수 p.
배열 자체를 가리키는 포인터 : int (*p)[n] : int형 n개가 모인 자료형을 가리키는 포인터 p.
구분 참 지랄같지만, 여튼 둘이 다르다는 것만 이해해 두자. 참고로 이런 구분방식은 함수 포인터에서도 나온다. 젭라…
그렇담 &&a 도 있을 수 있지 않을까? 미안하지만 그딴건 없다 -.- 대신 int (**)[3] 은 있다.
접어!