본문 바로가기

C언어

Chapter 3. 2차원 배열과 포인터

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_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]));
}

결과_3_7_2

함수인자 포인터
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차원 포인터 이것만은 알아두자
  1. 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 과 같이 사용하면 쉽고 빠르게 해당 배열의 요소에 접근이 가능하다.

  2. *imsip = *(imsip + 0)
    *imsip는 행 전체를 가리키며, *imsip는 현재 가리키고 있는 행의 가장 첫번째 배열 요소의주소 값이 들어 있다.
  3. imsip +i
    imsip의 i번째 제 1 부분 배열을 뜻한다. 이것은 수식이므로 배열의 특정한 위치를 나타낼 뿐 행 전체나 부분 배열 전체를 뜻하는 것이 아니다.
    행의 전체를 뜻하려면 위에서 본 것처럼 '*' 연산자가 필요하다.
  4. *(imsip + i)+ j
    *(imsip + i)를 이용하여 행을 가리키는 주소를 구할 수 있었고 이를 이용하여 행 전체 중에서 하나의 배열 요소를 구할 수 있는 수식이 바로 위의 수식이다.
    즉, *(imsip + i)라는 i번째 행을 가리키는 주소j를 더했으므로 이는 특정 배열 요소에 대한 주소가 되는 것이다.
  5. *(*imsip + i) + j)

    *(*(imsip + i) + j) == imsi[i][j]

    4번의 수식에서 구한 특정 배열 요소에 대한 주소에 '*' 연산자를 사용하여 2차원 배열에서 정수 값을 하나 취할 수 있고 위의 수식이 성립한다.
  6. **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)
}

결과_3_14

*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