오늘은 배열과 포인터를 좀 더 깊게 알아보는 시간입니다.
[2차원 배열, N차원 배열]
앞에서는 1차배열 int num[10]; 과 같은 배열을 다뤘습니다.
오늘은 int num[10][20]; 과 같은 1차원 배열을 넘는 배열에 대해서 다뤄보려고 합니다.
만약에 30명의 학생의 국영수 과목을 관리하는 프로그램을 만든다고 가정해봅시다.
그러면, 각각 1명 당 국영수 3과목에 대한 변수가 존재해야합니다.
또한, 30명의 이름이 필요합니다. 1명당 이름의 최대길이를 16 byte로 제한한다고 하면, 배열은
char student_name[30][16]; 이렇게 선언을 해야겠죠.
아래는 30명의 학생의 이름과 국영수 점수를 입력받은 뒤, 입력받은 숫자를 출력하는 예제 프로그램입니다.
#include<stdio.h>
#define ST_NUM 30 // 학생 수 30명으로 정의
void main()
{
int i = 0;
int st_math[ST_NUM] = {0,};
int st_kor[ST_NUM] = {0,};
int st_eng[ST_NUM] = {0,};
char st_name[ST_NUM][16];
for ( i = 0; i < ST_NUM; i++)
{
printf("%d번째 학생의 이름 : ", i+1);
scanf("%s", st_name[i]); // st_name[i] == &st_name[i][0]
fflush(stdin); // 문자열을 입력받은 뒤, 버퍼 비우기
printf("국영수 순으로 점수를 입력(ex: 10 20 30) : ");
scanf("%d %d %d", &st_kor[i], &st_eng[i], &st_eng[i]);
}
for ( i = 0; i < ST_NUM; i++)
{
printf("%s 점수 : 국[%d] 영[%d] 수[%d]\n", st_name[i], st_kor[i], st_eng[i], st_math[i]);
}
}
문자를 출력 및 입력 받을 때, %s 를 사용하고 매칭되는 것은 입출력할 문자열의 주소값이여야 합니다.
2차원 배열 char st_name[][]; 2차원의 배열을 선언하고
이것을 마치 1차원 배열 처럼 사용하면, 주소값을 의미합니다.
st_name == st_name[0] == &st_name[0] == &st_name[0][0] 모두 같은 의미입니다.
자 여기서 30명의 학급이 아닌, 30명의 학급이 있는 A / B / C 반의 성적을 입력받는다고 가정해봅시다.
그럼 배열이 1차원 더 증가합니다.
int st_kor[3][30];
char st_name[3][30][16]; 이럴 땐 for문을 이중으로 사용하면 편합니다.
#include<stdio.h>
#define CLASS_NUM 3 // 1반 2반 3반
#define ST_NUM 30 // 학생 수 30명으로 정의
void main()
{
int i = 0, j = 0;
int st_math[ST_NUM] = {0,};
int st_kor[ST_NUM] = {0,};
int st_eng[ST_NUM] = {0,};
char st_name[ST_NUM][16];
for ( i = 0; i < CLASS_NUM; i++)
{
printf("%d 반의 학생들 정보를 입력하세요\n", i+1);
for ( j = 0; j < ST_NUM; j++)
{
printf("%d번째 학생의 이름 : ", j+1);
scanf("%s", st_name[i][j]); // st_name[i] == &st_name[i][0]
fflush(stdin); // 문자열을 입력받은 뒤, 버퍼 비우기
printf("국영수 순으로 점수를 입력(ex: 10 20 30) : ");
scanf("%d %d %d", &st_kor[i][j], &st_eng[i][j], &st_eng[i][j]);
}
}
for ( i = 0; i < CLASS_NUM; i++)
{
printf("%d반 학생들의 정보입니다.\n" i+1);
for ( j = 0; j < ST_NUM; j++)
{
printf("%s 점수 : 국[%d] 영[%d] 수[%d]\n", st_name[i][j], st_kor[i][j], st_eng[i][j], st_math[i][j]);
}
}
}
그렇다면, 여기서 한 단계 더 나아가서 학년별로 범위를 넓히면 또 +1차원이 늘어납니다.
학교단위로 넓히면 +1 이렇게 N차원 배열이 될 수 있습니다.
이름과 같은 문자열은 이미 1차원 배열이므로, 다른 정수형 실수형 보다 +1차원이 더 많게 선언되어져서 쓰입니다.
[2차원 포인터, N차원 포인터]
지난 번에 int *num; 와 같이 1차원 포인터에 대해서 알아보았습니다.
오늘은 이제 한 단계 더 나아가서 2차원 포인터에 대해서 알아보겠습니다.
우선, 2차원 포인터 변수를 선언하면 1차원 포인터 변수의 주소값만을 넣을 수 있습니다.
즉 N차원 포인터 변수에는 N-1차원 포인터의 주소값만 저장할 수 있습니다. 예를 들어보겠습니다.
#include <stdio.h>
void main()
{
int num = 10;
int *p_num = #
int **pp_num = &p_num;
printf("num[%d] &num[%x], p_num[%x], *p_num[%d], &p_num[%x]\n",
num, &num, p_num, *p_num, &p_num);
printf("&pp_num[%x], pp_num[%x], *pp_num[%x], **pp_num[%d]\n",
&pp_num, pp_num, *pp_num, **pp_num);
}
위의 그림과 같은 구조를 가지게 됩니다.
**pp_num 은 2번 참조하라는 겁니다. * 한 번 갔더니 p_num에는 &num 의 주소값이 있죠? &num 의 주소값은 참조하는거니 num 이 되게 되는겁니다.
좀 더 쉽게 이해하시려면 *p_num 은 사실 *&num 과 같죠? *이 참조하면 &을 없애버립니다. 즉, num 이 되는거죠.
**pp_num 을 비슷하게 대입해봅시다. pp_num 에는 &p_num의 주소값이 있기 때문에 **&p_num 이 됩니다.
**&p_num == *p_num 이 되고, 위에서와 똑같이 num 이 됩니다.
num[i][j] == *( *(num + i ) ) + j) 이렇게 사용할 수 있습니다.
앞장에서 포인터 변수에 + 는 ( X 자료형의 크기) 라고 말씀드렸었습니다.
배열을 포인터로 바꾸고, 포인터를 배열로 바꾸는 걸 연습을 많이 해보시는 걸 추천드립니다.
포인터는 C언의 꽃이라고 불릴만큼(제가 C언어를 배울 때 C언어를 가르쳐준 선배의 말입니다.^^) 엄청 중요하면서도 유용한 개념이지만 이해하기가 쉽지 않습니다.
다들 배열과 포인터에서 C언어가 너무 어렵다며 포기하거나... 힘들어합니다.
하지만, 이것만 명심하세요. 포인터 변수의 원론의 정의만 잊지 말고 단계 별로 논리적으로 생각하면 좀 더 쉬우실 겁니다.
★ 포인터 변수는 주소값만(Only)을 저장할 수 있는 변수이다.
=> N차 포인터 변수는 N-1차 포인터변수의 주소값을 저장하는 거겠죠. 1차는 변수의 주소값이고요.
★ * 은 참조의 뜻입니다. 즉, 해당 포인터 변수의 주소값을 찾아갑니다. 포인터 변수에 어떤 변수의 주소값이 있고 *은 그 주소를 따라가서 문을 두드리는 상상을 해보세요. 마치 우편배달부처럼요. 그럼 좀 더 쉽게 이해가 되실까요??^^
그럼 배열과 포인터 포스팅을 마치겠습니다. 포인터는 뒤에 함수라는 것을 배우면서 또 등장할 것 같습니다.
그럼 20000.
9강 C언어 구조체 (0) | 2020.01.30 |
---|---|
8강 C언어 함수(Function), API (0) | 2020.01.29 |
6강 C언어 포인터의 개념과 사용 (0) | 2020.01.28 |
5강 C언어 배열의 개념과 사용 (0) | 2020.01.23 |
4강 C언어 반복문, for문, while문, do~while문 (0) | 2020.01.21 |