본문 바로가기

코딩 농장/C 언어

C언어 배열과 포인터 _ 2 (이중 배열 / 포인터)

시작하기 앞서 배열을 선언하는 포맷을 살펴보자.

<1차원 배열 포맷>

int a[] = { 1, 2, 3 };
int b[3] = { 1, 2, 3 };
int c[3] = {0};
int d[3];

→ 배열 a[] 에 1, 2, 3 을 넣으면 '아 배열 a 의 크기는 3 이구나' 라고 인식한다. 따라서 배열의 크기를 생략해도 된다.

→ c[3] 의 원소를 0 으로 초기화 시켜주었다. 자동으로 c 값의 원소는 {0, 0, 0} 으로 된다. ({0} 으로만 써도 됨)

→ d[3] 배열을 생성해 주고 밑에서 초기화를 시켜주어 값을 넣어주어야 사용할 수 있다.

<2차원 배열 이해>

int x[3];
int y[3][2];

→ 첫번째 배열 x 는 '배열의 이름은 x 이고 배열 x 의 3개의 각 원소는 int 형이다.' 을 의미한다.

→ 두번째 배열 y는 '배열의 이름은 y 이고 배열 y 의 3개의 각 원소는 int 형 원소를 2개 가지는 배열이다.' 을 의미한다.

여기서 말이 좀 혼동될 수 있지만, 짚고 넘어가자면  int 형 배열 = 배열의 원소는 모두 int 형이다 라는 뜻이다. 위 문장을 다시 써보자면

→ 첫번째 배열 x 는 '배열의 이름은 x 이고 int 형 배열이다.' 을 의미한다. 

→ 두번째 배열 y는 '배열의 이름은 y 이고 배열 y 의 3개의 각 원소는 원소를 2개 가지는 int 형 배열이다.' 을 의미한다.

위와 같은 말이지만 이중 배열을 이해하기 위해 같은 의미 다른 표현을 써보자면

→ 첫번째 배열 x 는 '3칸의 배열이고 그 원소는 int 형이다.'  // 해독한 순서 [3] = int x

→ 두번째 배열 y 는 '3칸의 배열이고 그 배열안에 2칸의 배열이 있고 그 원소는 int 형이다.' // 해독한 순서 [3] > [2] = int y 

                                    y

         y[0]                    y[1]                   y[2]

 y[0][0]     y[0][1]    y[0][0]    y[0][1]    y[0][0]    y[0][1] 

→ 위 도표처럼 배열 y 는 3개의 int 형 배열을 갖고 있고 각 배열들은 2가지 원소들이 있다.

<2차원 배열 포맷>

int a[][2] = { {1, 2}, {3, 4}, {5, 6} };
int b[][2] = { 1, 2, 3, 4, 5, 6 };
int c[3][2] = { {1, 2}, {3, 4}, {5, 6} }; // int c[3][2] = {1, 2, 3, 4, 5, 6};

→ 배열 a 는 원소의 개수가 2개인 int 형 배열을 갖고 있다. 그 배열이 몇개인지는 초기화 해준 값을 보고 자동으로 계산이 되서 생략 가능하다.

→ 배열 b 또한 원소의 개수가 2개인 int 형 배열을 갖고 있다. int 형 배열의 원소의 개수가 2개 이기 때문에 초기화 해준 값을 보고 2개씩 묶어서 int 형 배열이 몇개인지 계산할 수 있어 int 형 배열의 개수를 생략 가능하다.

→ 배열 c 처럼 다 써주어도 된다.

<2차원 배열과 이중 포인터 이해>

int *a[3] = { int*, int*, int* };
int (*a)[3] = {{ int, int, int }, { int, int, int } ...}; // int a[][3]; 과 동일
// 여기서 (*a) 는 { int, int, int } 하나를 가리킴

→ 첫번째 줄을 보면 '3칸의 배열이 있고 그 원소는 포인터이다.' 라는 뜻이라서 배열 이름 a 는 포인터형 배열 3개를 원소로 갖는다.

→ 두번째 줄을 보면 'a 라는 포인터는 배열을 가리키고 그 배열은 3칸의 배열이고 그 원소는 int 이다.' 라는 뜻이다. // 해독한 순서 (*a) > [3] = int

<int *a[3]>

int* p = { 0 }, * q = { 0 }, * r = { 0 };
	int* a[3] = { p, q, r };
	printf("%d\n", sizeof(a));

→ 배열 a 의 원소들은 포인터이기 때문에 sizeof(a) 는 4 * 3 = 12 이다.

	int b[2] = { 0, 1 };
	int* p = &b, * q = { 0 }, * r = { 0 };
	int* a[3] = { p, q, r };
	printf("%d\n", sizeof(a));

→ 또 p 포인터가 b 배열을 가리키고 있다고 하여도 마찬가지로 배열 a 의 원소들은 그대로 포인터이기 때문에 12 이다.

<int (*a)[3]>

	int b[][3] = { {0, 1, 2}, {0, 1, 2} };
	int(*a)[3] = &b;

	printf("%d\n", sizeof(b));
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof((*a)));

→ 배열 이름 b 는 배열 전체의 데이터를 갖고 있어 4 * 6 = 24 의 크기를 갖는다.

→ 포인터 a 는 현재 원소가 3개인 배열을 가리키고 있는 주소값이므로 4 의 크기를 갖는다.

→ 포인터 a 의 역참조 (*a) // a 의 주소값의 데이터는 3칸짜리 배열이므로 4 * 3 의 크기를 갖는다.

	printf("%d\n", a[0]);
	printf("%d\n", a[0][1]);

→ 포인터 a 는 원소가 3개짜리 배열의 주소값을 가리키는데 %d 로 배열을 printf 하라는 것은 잘못되서 쓰레기 값이 나온다.

→ 포인터 a 가 가리키는 배열의 [1] 번째 원소를 출력하면 1 이 출력된다.

'코딩 농장 > C 언어' 카테고리의 다른 글

C++ 프로그래밍  (0) 2021.09.26
VisualStudioCode 개발환경 구축 (MinGW, JSON,gcc,compile)  (0) 2021.09.16
C언어 배열 / 포인터 연산  (0) 2021.07.06
C언어 배열과 포인터  (0) 2021.07.06
C언어 연산자  (0) 2021.07.06