2017년 4월 28일 금요일

C언어 배열/주소 개념 정리 arr+1, arr[0]+1,&arr[0]+1, &arr+1, &arr[0][0]+1 , 그리고 배열명의 두 가지 의미

포인터를 제대로 잡고가기 위해서는 배열의 정확한 개념이해가 필수적이다.
배열은 주소(address) 와 굉장히 연관이 있기에 배열 개념을 제대로 잡으면, 포인터가 좀더 쉬워질 수 있다.

먼저 2차원  int 배열을 선언 하였다고 가정하고 값이 아래와 같다고 생각하자 .

int arr[2][3] = {{1, 2,3}, {4,5,6}};

문제를 내겠다. 문제를 전부 맞춘다면, 배열의 개념에대해 정확히 알고 있는 것이므로, 이 글을 생략하거나,
가볍게 읽고 다시 복습 하는 개념으로 읽으면 도움이 될 것이다.

arr의 주소가 1000 이라고 가정하자.
(앞의 4자리가 귀찮고, 16진수가 불편하니 10진수로 답을 적고 문제를 내겠음.)

printf("%p", arr)    ==>   1000  이라고 가정하면
=========================
1. arr = ?
2. arr[0] = ?
3. &arr = ?
4. &arr[0] = ?
5. &arr[0][0] = ?
=========================
위 다섯 개 답은 무엇일까 ?




모두 1000 이다. 그런데 무엇이 차이가 있을까? 같은 주소 1000을 가리키는데 왜 이렇게 표현 방법이 많을까. 헷갈 릴 수 있다.

아래 문제를 풀면 왜 다 다른지 알수 있다.

========================
6.  arr+1 = ?
7.  arr[0] + 1 =?
8.  &arr + 1 = ?
9.  &arr[0] + 1  =?
10. &arr[0][0] + 1 = ?
11. arr[0][0] +1 = ?
12. sizeof(arr)
13. sizeof(arr[0])
14. sizeof(arr[0][0])
15. sizeof(&arr)
16. sizeof(&arr[0])
17. sizeof(&arr[0][0])
========================






6.  1012 , 7.  1004,  8.   1024,    9.   1012,    10.   1004,    11.    2
12.  24,   13.   12,   14.   4     15.     4,    16.    4,    17.   4

이다.

여기까지 당연하게 쉽게 쉽게 푼다면, 배열의 개념과 주소의 개념을 정확히 알고 있는 것이라고 할 수 있다.
그럼 이 글을 스킵하거나, 가볍게 복습 겸 읽으면 된다.


배열의 이름 (배열명) 이 의미하는 것은 두가지 이다. ((*매우 중요))
1. 배열 전체
2. 배열의 첫번 째 인자의 시작 주소이다.

1번이 적용되는 가장 큰 경우는 sizeof(arr)의 경우이다.  int arr[2][3] 으로 선언 되어있으므로,
int가 4byte이고, int형태가 2행 3열로 되어있는 것이므로 4byte * 2 * 3  = 24byte 로
24 라는 결과 값을 얻게 될 것이다.
(int의 데이터 값은 cpu의 bit에 따라 값을 달리 할 수 있으므로, 참고한다. 많이 쓰는 일반적인 경우가
4byte 임.)

1. arr = ?
2. arr[0] = ?
3. &arr = ?
4. &arr[0] = ?
5. &arr[0][0] = ?

앞서 보았던 문제의 값이 다 1000이다.
그런데 이것이 의미하는 것이 조금 씩 다른 것이다.
1부터 보면, 배열명이 의미하는 두번째 의미가 적용된다. '배열의 첫번 째 인자의 시작 주소' 이다.
배열이라는 개념이 시작주소부터 한칸씩 이동하는 개념인데 +1, +2 라는 개념을 보기에 앞서,
 이 개념을 1차원 배열로 접근해보자.
1차원 배열에서 는 int arr2[3] = {7,8,9} 라고 할 때,  7, 8, 9 라는 데이터에  arr2[0], arr[1], arr[2] 로 접근할 수 있고, 이것의 주소의 접근에는 &arr2[0], &arr2[1], &arr2[2] 로 접근 할 수 있다.
이 1차원 배열에서  sizeof(arr2) 라고하면, 배열명의 첫 번 째 의미인 배열전체를 의미하게되어, 12가 나올 것이고,  arr2를 단순히 출력하라고 한다면, 이것은 arr2의 시작주소를 출력 할 것이다. arr2의 시작주소가 2000 이였다고 가정해보자.  arr2 + 1 을 출력하면 몇이 나올까?
 답은 2004가 나올 것이다. 여기선 배열명의 두 번 째 의미인, '배열의 첫번 째 인자의 시작주소' 를 의미한다. 이 말이 무슨 의민지 정확히 이해가 안갈 수 있는데, 정확히 똑같은 값을 의미하는 것이 무엇이냐면
arr2  == &arr2[0] 이다. 즉,  arr2[0] (7을 가지고 있는 int ) 의 주소 2000을 가리킨다. 4byte 범위의 시작주소 2000이라는 것이다. 그래서 arr2+1 은 2004이다.
그럼 &arr2+1 은 무엇이 나올 것인가?
답은 2012이다. 그런데 이것은 헷갈릴 수 있다. arr2라는 arr2[3] 의 배열명인 arr2가 배열 전체를 의미한다며? 라고 반문 할 수 있는데, 주소를 접근하는 개념에서 배열명은 첫번 째 인자의 시작 주소라는 것을 머리속에 깊이 새겨놔야한다. 접근하고자 한다면 접근할 수 있지만, 배열이 12범위를 차지하는데 그범위를 넘어선 영역을 왜 접근하겠나? .. 라는 생각을 하면, 수긍이 갈 것이다. 일반적으로 접근할리가 없기에 &arr이라는 문자를 사용해야 &arr이 주소 개념에서 12 byte를 차지한다. 그래서 &arr + 1 은 2000 + 12 인 2012가 된다.

 1차원 배열은 여기까지 이야기하고 정리를 하겠다.

-----------------------------------------------------------------------------------
              2000       2004     2008        (주소)  
 int arr2 =       {  7           8            9 }

arr2 == 2000 ( arr2[0] 의시작주소 인 2000 (4byte의미))
 따라서 arr2 + 1 == 2004

arr2[0] == 7   (배열의 인자인 arr2[0] 자체는 그 값을 의미)
 따라서 arr2 + 1 == 8  (여기서 8은 다음인자 8을 가리키는 것이아니라 단순히 값에 1을 더한 8임)

&arr2 == 2000 (arr2 배열전체의 시작주소인 2000 (12byte의미))
 따라서 &arr2 + 1 == 2012  (이것이 배열을 벗어나는 의미이므로 사실상 다른영역에 대한 접근임)

&arr2[0]  == 2000 ( 배열의 인자인 arr2[0] 의 주소 arr2[0] 은 4byte의 int형태)
 따라서 &arr2[0] + 1 == 2004  == &arr2[1]

-----------------------------------------------------------------------------------


이제 2차원 배열을 봐보자.

int arr[2][3]
1000            1004         1008
       {       1             2               3       }
1012             1016          1020
       {       4            5               6       }

이렇게 나타낼 수 있다. 그리고 위에 주소들은 각각 인자의 시작주소라고 인지하자.
글로 풀어 쓰면
&arr[0][0] == 1000 ,  &arr[0][1] == 1004 ,  &arr[0][2] == 1008
&arr[1][0] == 1012    ,  &arr[1][1] ==  1016  ,  &arr[1][2] == 1020
이다.

2차원 배열을 다른 생각으로 접근해보자. arr은 arr[0], arr[1] 이라는 두개의 인자를 가진 배열이다.
그리고 arr[0] 는 1 ,2 ,3 int형 (4byte)를 3개 가지고 있는 12byte 짜리 형태인것이다.
다시 말해 arr은  int [3] 형태의 인자를 두개 가지고 있는 1차원 배열이다. 라고 생각할 수 있다.
그렇게 접근하면 위의 1차원 배열과 같이 쉽게 이해 할 수 있다.
arr 이라는 값을 출력하게 되면 , 그것은 arr배열 {arr[0], arr[1]} 두 인자를 가진 인자의 첫번 째 인자의 값.
즉 arr[0] (12byte)의 주소 인 1000이다 . 그래서 arr+ 1 은 1012 라는 값을 갖는다.
그리고 arr[0] 라고 그 값을 출력하면 역시나 1000이 뜨는데 arr[0]라는 것은 {1, 2, 3} 인자를 가진 배열의 배열 명 이라고 할 수 있다. 그래서 arr[0] 라는 것은 첫번 째 인자인 1 (4byte), 즉 arr[0][0] 의 주소인
&arr[0][0] 로써의 1000이라는 결과 값을 갖는다. arr[0]+1 은 그래서 1004 라는 값을 갖는다.
그리고 최하위인 arr[0][0] 는 주소가 아닌 1 이라는 값 이다.

문제를 하나 하나 짚어보자.

6.  arr+1
arr 은 배열 arr의 첫번 째인자인 arr[0] 의 주소 즉 arr[0]+1 은 12byte의 arr[0]의 다음인 1012를 의미

7.  arr[0] + 1
arr[0]는 {1, 2, 3} 의 배열명임. 즉 arr[0]의 첫번째 인자는 arr[0][0]인 1이고 , 이것은 int형으로
+1 한 결과는 1004가 됨.

8.  &arr + 1
주소개념에서 &를 붙인 &arr은 (24byte)의 시작주소인 1000이다. 그래서 &arr+1 을 하게되면 1024가 된다.

9.  &arr[0] + 1
arr[0]라는 {1,2,3}의 배열명에 &을 붙이면 그 배열 전체인 12byte를 의미해서 +1 을하면 1012가 된다.

10. &arr[0][0] + 1
&arr[0][0] 은 2차원배열에서 1을 값으로 갖는 int 형의 주소이다. 이 경우는 일반적으로 많이 쓰므로 +1 의 결과가 1004 라는 것을 알 것이다.

11. arr[0][0] +1
arr[0][0]은  arr 2차원 배열 의  1이라는 값이다. 거기에 +1을 했다는 것은 1이라는 값에 +1을 해서 2가 된 값이다. arr[0][1] 이 절대 아님..

12. sizeof(arr)
arr은 배열명의 첫번 째 의미로써 작용되고, 24byte 24 의 결과가 나온다.
13. sizeof(arr[0])
arr[0]는 1,2,3을 포함하는 첫번째 행이고, int 형 3개를 포함하는 배열로 12가 나온다.
14. sizeof(arr[0][0])
sizeof(int)와 같은 의미고, 정확히는 sizeof(1)을 한 것이다. int 형의  크기 4 가 나온다.
15. sizeof(&arr)
&가 붙은건 무조건 주소이다. 주소 (포인터) 의 크기는 4이다.
16. sizeof(&arr[0])
&가 붙은건 무조건 주소이다. 주소 (포인터) 의 크기는 4이다.
17. sizeof(&arr[0][0])
&가 붙은건 무조건 주소이다. 주소 (포인터) 의 크기는 4이다.











댓글 1개: