Chapter 3. 2차원 배열과 포인터
미리보기
3_1.c
#include <stdio.h>
main()
{
int imsi[3][2] = {{6,3}, {9,1}, {7,2}};
int (*imsip)[2];
int i,j;
imsip = imsi;
for(i=0;i<3;i++)
for(j=0;j<2;j++)
printf("[%d][%d] %d\n", i, j, *(*(imsip+i)+j));
printf("----------------------\n");
for(i=0;i<3;i++)
for(j=0;j<2;j++)
printf("[%d][%d] %d\n", i, j, *(imsip[i]+j));
printf("----------------------\n");
for(i=0;i<3;i++)
for(j=0;j<2;j++)
printf("[%d][%d] %d\n", i, j, imsip[i][j]);
printf("----------------------\n");
for(i=0;i<3;i++)
for(j=0;j<2;j++)
printf("[%d][%d] %d\n", i, j, imsi[i][j]);
}
gcc -o 3_1 3_1.c
./3_1
[0][0] 6
[0][1] 3
[1][0] 9
[1][1] 1
[2][0] 7
[2][1] 2
----------------------
[0][0] 6
[0][1] 3
[1][0] 9
[1][1] 1
[2][0] 7
[2][1] 2
----------------------
[0][0] 6
[0][1] 3
[1][0] 9
[1][1] 1
[2][0] 7
[2][1] 2
----------------------
[0][0] 6
[0][1] 3
[1][0] 9
[1][1] 1
[2][0] 7
[2][1] 2
배열 포인터의 정의
int *imsip; // 1차원 배열 포인터
int(*imsip2)[2]; // 2차원 배열 포인터
int (*imsip3)[2][3];// 3차원 배열 포인터
(배열)포인터 변수는 무조건 4byte가 할당된다. 단지, 가리키는 대상체가 1차원이냐 2차원이냐에 따라서 정의하는 형식이 달라지는 것일 뿐이다.
2차원 배열 포인터에2차원 배열 할당
int imsi[2][3];
int (*imsip)[3];
imsip = imsi;<-- 2차원 배열 포인터 변수에2차원 배열의 주소 할당
3_2.c
#include <stdio.h>
main()
{
int imsi[2];
int *imsip;
int imsi2[2][3];
int (*imsip2)[3];
imsip = imsi;
imsip2 = imsi2;
printf("%d\n", sizeof(imsi));
printf("%d\n", sizeof(imsip));
printf("%d\n", sizeof(*imsip));
printf("-----------------\n");
printf("%d\n", sizeof(imsi2));
printf("%d\n", sizeof(imsip2));
printf("%d\n", sizeof(*imsip2));
}
gcc -o 3_2 3_2.c
./3_2
8
4
4
-----------------
24
4
12
포인터배열의 주소 할당의 차이
같은주소 값을 가지고같은 곳을 가리키고 있지만 대상체가 다르므로 성립되지 않는다.
3_4.c
#include <stdio.h>
main()
{
int imsi[3][2] = {{3, 5}, {12, 54}, {534, 923}};
int (*imsip)[2];
imsip = imsi;
imsip = imsi[0];
imsip = &imsi[0][0];
}
gcc -o 3_4 3_4.c
3_4.c: 9: warning: assignment from incompatible pointer type
3_4.c: 10: warning: assignment from incompatible pointer type
대상체가 어떻게 다른지 확인해보자.
3_5.c
#include <stdio.h>
main()
{
int imsi[3][2] = {{3, 5}, {12, 54}, {534, 923}};
int (*imsip)[2];
printf("imsi : %d byte\n", sizeof(imsi));
printf("imsi[0] : %d byte\n", sizeof(imsi[0]));
printf("&imsi[0][0] : %d byte\n", sizeof(&imsi[0][0]));
}
gcc -o 3_5 3_5.c
./3_5
imsi : 24 byte
imsi[0] : 8 byte
imsi[0][0] : 4 byte
즉, imsi는 배열전체를 가리키고 imsi[0]는 행을 대표하고 있으며 &imsi[0][0]은 하나의 배열 요소를 가리키고 있음을 확인 할 수 있다.
imsip = imsi
포인터 변수는 배열 중에서 가장 첫번째 요소를 가리키고 다른 요소들은 포인터 연산을 이용하여 참조하게 된다.
1차원 포인터 변수는 단지 하나의 배열 요소만을 대상체로 하고, 2차원 배열 포인터 변수는 행을 대상체로 한다.
1차원 포인터 변수가 가리키는 대상은 4byte며2파원 배열 포인터 변수가 가리키는 대상은 8byte 이다.
int imsi[2][3]의 모든것
아래프로그램의 결과값을예상해보자.정확히 답변하지 못한다면완전하게 알고 있는 것이 아니다.
3_7_1.c
#include <stdio.h>
main()
{
int imsi[2][3];
printf("%x %d\n", imsi, sizeof(imsi));
printf("%x %d\n", &imsi, sizeof(&imsi));
printf("%x %d\n", imsi[0], sizeof(imsi[0]));
printf("%x %d\n", &imsi[0], sizeof(&imsi[0]));
printf("%x %d\n", imsi[1], sizeof(imsi[1]));
printf("%x %d\n", &imsi[1], sizeof(&imsi[1]));
printf("%x %d\n", imsi[2], sizeof(imsi[2]));
printf("%x %d\n", &imsi[2], sizeof(&imsi[2]));
printf("%x %d\n", imsi[0][0], sizeof(imsi[0][0]));
printf("%x %d\n", &imsi[0][0], sizeof(&imsi[0][0]));
printf("%x %d\n", imsi[0][1], sizeof(imsi[0][1]));
printf("%x %d\n", &imsi[0][1], sizeof(&imsi[0][1]));
}
3_7_2.c
#include <stdio.h>
main()
{
int imsi[3][2];
printf("*imsi : %x\t%d\n", *imsi, sizeof(*imsi));
printf("*imsi[0] : %x\t%d\n", *imsi[0], sizeof(*imsi[0]));
printf("(*imsi)[0] : %x\t%d\n", (*imsi)[0], sizeof((*imsi)[0]));
}
함수인자 포인터
2차원포인터 다루기
1차원 배열 요소의 값을포인터를 이용하여 얻기 위해서는'*'가 하나.
2차원 배열에서는 '*'가두개 있어야배열 요소의 값을 취할 수 있음.
imsip // imsi
imsip+1 // imsi+1
imsip+2 // imsi+2
*imsip // imsi[0]
*(imsip+1)// imsi[1]
*(imsip+2) // imsi[2]
**imsip // imsi[0][0]
*(*imsip+1) // imsi[0][1]
*(*(imsip+1)) // imsi[1][0]
*(*(imsip+1)+1)// imsi[1][1]
*(*(imsip+2)) // imsi[2][0]
*(*(imsip+2)+1)// imsi[2][1]
배열포인터에서의 '*' 는배열에서 대괄호를 이용한 첨자를 사용한 것과 같다.
2차원 배열에서 imsi[0], imsi[1], imsi[2]는 행의 주소를 뜻한다고 했으므로,
배열포인터에서 '*'연산자를 하나만 붙여서는 배열 요소의 값을 취할 수 없ㅇ고 행의 주소를 얻어온다.
2차원 배열을 포인터로 접근했을 때 '*' 하나는 행을 대상체로 하고있기 때무에 절대배열 요소 자체의 값을취할 수 없다.
3_7.c
#include <stdio.h>
main()
{
int imsi[3][2] = {{3, 5}, {12, 54}, {534, 923}};
int (*imsip)[2];
imsip = imsi;
printf("*imsp\t*imsip+0 : %#010x\t%#010x\n", *imsip, *imsip+0);
printf("*(imsp+1)\t*imsip+1 : %#010x\t%#010x\n", *(imsip+1), *imsip+1);
printf("*(imsp+2)\t*imsip+2 : %#010x\t%#010x\n", *(imsip+2), *imsip+2);
}
gcc -o 3_7 3_7.c
./3_7
*imsp*imsip+0 : 0xbfd387f8 0xbfd387f8
*(imsp+1) *imsip+1 : 0xbfd38800 0xbfd387fc
*(imsp+2) *imsip+2 : 0xbfd38808 0xbfd38800
2차원 배열에서 포인터 변수더하기 정수는 행 단위로 움직인다.
3_8.c
#include <stdio.h>
main()
{
int imsi[3][2] = {{6, 3}, {5, 9}, {8, 2}};
int (*imsip)[2] = imsi;
//배열의 요소
printf("imsip+0 : %#010x\t %d byte\n", imsip+0, sizeof(imsip+0));
printf("imsip+1 : %#010x\t %d byte\n", imsip+1, sizeof(imsip+1));
printf("imsip+2 : %#010x\t %d byte\n", imsip+2, sizeof(imsip+2));
// 배열의 행
printf("*(imsip+0) : %#010x\t %d byte\n", *(imsip+0), sizeof(*(imsip+0)));
printf("*(imsip+1) : %#010x\t %d byte\n", *(imsip+1), sizeof(*(imsip+1)));
printf("*(imsip+2) : %#010x\t %d byte\n", *(imsip+2), sizeof(*(imsip+2)));
}
gcc -o 3_8 3_8.c
./3_8
imsip+0 : 0xbf852b18 4 byte
imsip+1 : 0xbf852b20 4 byte
imsip+2 : 0xbf852b28 4 byte
*(imsip+0) : 0xbf852b18 8 byte
*(imsip+1) : 0xbf852b20 8 byte
*(imsip+2) : 0xbf852b28 8 byte
같은주소 값을갖고 있지만 대상체가 다르다.
imsip+0은 단지 imsip가 가리키는 곳에서 0번째떨어진 곳의 주소를 뜻하고, *(imsip+0)은 imsip에서 0번째 떨어진 대상체를 뜻한다.
2차원 포인터 이것만은 알아두자
- imsip
intimsi[3][2] = {{3, 2}, {5, 6}, {12, 43}}
int (*imsip)[2];
여기서 2는 행을 뜻하는것이 아니라 열의 개수를 뜻한다.
행의 개수만을 지정하면 열의개수를 알 수 없다. 하지만, 열의 개수를 지정하면 자동적으로 행의 개수를 추정할 수 있으므로 위의 정의가 가능한 것이다.
int imsi[50][30] ={...};
int*imsip =&imsi[0][0];
2차원 배열을 1차원 포인터 변수에 할당하여사용할 수 있다.
이런 경우 imsip는 1차원적으로 배열들을 다루게 된는 것을 의마한다.
즉, imsip에 1을 더한다는 것은행 단위로 움직이는 것이 아니라 배열의 요소 단위로 움직인 다는 것을 뜻하며, 다음과 같은 수식만이 가능할 뿐이다.
imsip +i [가능]
imsip[i] +j [불가능]
(imsip+ i) + j [불가능]
또한, 2차원 배열을 1차원 포인터 변수에 할당하여 사용할 경우배열 요소에 대한 접근 어려워지게 된다.
만약, 위의 배열에서 27행 19열의 배열 요소를 출력하고 할 경우 1차원 배열로 할당하여 사용할 경우imsip +798과 같이배열 요소의 위치를계산하여야 한다.
하지만, 2차원 포인터 변수를 사용할 경우*(imsip + 26)+ 18 과 같이 사용하면 쉽고 빠르게 해당 배열의 요소에 접근이 가능하다.
- *imsip = *(imsip + 0)
*imsip는 행 전체를 가리키며, *imsip는 현재 가리키고 있는 행의 가장 첫번째 배열 요소의주소 값이 들어 있다. - imsip +i
imsip의 i번째 제 1 부분 배열을 뜻한다. 이것은 수식이므로 배열의 특정한 위치를 나타낼 뿐 행 전체나 부분 배열 전체를 뜻하는 것이 아니다.
행의 전체를 뜻하려면 위에서 본 것처럼 '*' 연산자가 필요하다. - *(imsip + i)+ j
*(imsip + i)를 이용하여 행을 가리키는 주소를 구할 수 있었고 이를 이용하여 행 전체 중에서 하나의 배열 요소를 구할 수 있는 수식이 바로 위의 수식이다.
즉, *(imsip + i)라는 i번째 행을 가리키는 주소에 j를 더했으므로 이는 특정 배열 요소에 대한 주소가 되는 것이다. - *(*imsip + i) + j)
*(*(imsip + i) + j) == imsi[i][j]
- **imsip
**imsip == *(*(imsip +0) +0)
*imsip는 2차원 배열 중에서 가장 첫번째 부분 배열을 지칭하고 **imsip는 첫번째 부분 배열 중에서 첫번째 배열 요소를 지칭한다.
int(*imsi)[2], int *temp[2]의 차이
int (*imsi)[2] 는2차원 배열을 가리킬 수 있는 2차원 배열 포인터 변수가 메모리에 4byte '딱 하나' 생성된다.
int *temp[2]는 포인터 변수가 '두개' 생성되고 메모리에도'두 변수'에 대한 할당이 이루어진다.
3_13.c
#include <stdio.h>
main()
{
int (*imsip)[2];
int *temp[2];
printf("&imsip : %#010x\n", &imsip);
printf("&temp[0] : %#010x\n", &temp[0]);
printf("&temp[1] : %#010x\n", &temp[1]);
printf("sizeof imsip : %d byte\n", sizeof(imsip));
printf("sizeof temp : %d byte\n", sizeof(temp));
}
gcc -o 3_13 3_13.c
./3_13
&imsip : 0xbff9ba70
&temp[0] : 0xbff9ba68
&temp[1] : 0xbff9ba6c
sizeof imsip : 4 byte
sizeof temp : 8byte
*temp[2]는 주소를 저장할 수 있는 공간이하나가 아닌 두 개가 만들어 진다.
3_14.c
#include <stdio.h>
main()
{
int imsi[3][2] = { {3, 5, }, {12, 54}, {534, 923} };
int (*imsip)[2];
int *temp[2];
imsip = imsi; // (1)
imsip = &imsi[0][0];// (2)
temp[0] = imsi; // (3)
temp[0] = imsi[0]; // (4)
temp[1] = imsi[1]; // (5)
temp[2] = imsi[2]; // (6)
temp[0] = *(imsi + 0); // (7)
temp[1] = *(imsi + 1); // (8)
temp[2] = *(imsi + 2); // (9)
*temp = imsi[0];// (10)
*(temp + 0) = imsi[0]; // (11)
*(temp + 1) = imsi[1]; // (12)
*(temp + 2) = imsi[2]; // (13)
*temp = *imsi;
*(temp + 0) = *(imsi + 0); // (14)
*(temp + 1) = *(imsi + 1); // (15)
*(temp + 2) = *(imsi + 2); // (16)
}
*temp[3]에서temp으 의미는?
temp는 배열명이다. 배열명 자체는 주소 값을 뜻하며 이 주소는 temp[0]이 저장된 곳의 주소를 말한다.
따라서, temp[0]과&temp[0], temp의 출력 값은 같게 된다.
하지만, 이들을 같다고 생각하면 안된다. temp는 temp[0], temp[1], temp[2]를 대표하는 모 배열이기때문이다.
3_15.c
#include <stdio.h>
#define print(imsi) printf(#imsi "\t%08x %d\n", imsi, sizeof(imsi))
main()
{
int imsi_1[2] = {5, 7};
int imsi_2[3] = {45, 67, 25};
int imsi_3[4] = {27, 34, 87, 25};
int *temp[3];
temp[0] = imsi_1;
temp[1] = imsi_2;
temp[2] = imsi_3;
print(temp);
print(&temp);
print(*temp);
print(temp[0]);
print(&temp[0]);
}
gcc -o 3_15 3_15.c
./3_15
temp bfacb574 12
&temp bfacb574 4
*temp bfacb59c 4
temp[0] bfacb59c 4
&temp[0] bfacb574 4
(*imsi)[2]와 *temp[2]에서 imsi와 temp의 용어 정의를 명확히 해보자.
imsi는2차원 배열을 가리킬 수 있는 "2차원 배열 포인터 변수"라고용어를 지칭할 수 있고
temp는 포인터를 가리킬수 있는 요소 두 개를 가지고 있는 "포인터 배열"로지칭 할 수 있다.
'C언어' 카테고리의 다른 글
Chapter 6. scanf()와 fgets() (0) | 2011.10.16 |
---|---|
Chapter 2. 1차원 배열과 포인터 (0) | 2011.10.16 |
실행결과 (0) | 2011.10.16 |
문자열관련 예제 (0) | 2011.10.16 |
Chapter 1. 포인터의 기초 (0) | 2011.10.16 |