시작하기 앞서 배열을 선언하는 포맷을 살펴보자.
<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 |