티스토리 뷰
본 강좌는 아래 동영상 강좌와 같이 진행됩니다. 되도록이면 동영상과 같이 보시는 것을 추천합니다.
이번시간에는 여러 자료형을 묶어 하나로 사용할 수 있는 구조체에 대해 알아보도록 하겠습니다.
1. 구조체
학생 10명의 국어 점수를 저장하고 싶을때는 다음과 같이
int scoreKOR[10];
이렇게 10개만큼의 배열로 선언을 해줬습니다. 여기서 국어 점수 뿐만 아니라 수학, 영어 점수도 저장하고 싶을때는 아래처럼
int scoreKOR[10];
int scoreMAT[10];
int scoreENG[10];
int scoreSCI[10];
지난시간까지 알아본 것을 이용하면 각각의 배열로 따로 선언해줄수밖에 없습니다. 이렇게 선언하게 되면 배열의 갯수가 많아질수록 관리하기가 어려워지고 코드를 알아보기에도 좋지 않습니다.
이럴때는 아래와 같이 구조체를 사용합니다.
선언할때
struct 구조체이름
{
자료형 변수명;
자료형 변수명;
...
}
'구조체'란 여러 자료형을 가진 변수들을 하나로 묶어 자료형으로 사용할 수 있도록 정의하는 것을 말합니다. 위처럼 'struct' 뒤에 구조체의 이름을 정해주고, 중괄호 블럭 안에 필요한 변수들을 선언하여 사용합니다.
위의 배열로 든 예를 구조체로 정의해보면
struct score
{
int scoreKOR;
int scoreMAT;
int scoreENG;
int scoreSCI;
}
이처럼 할 수 있습니다.
구조체를 정의하는 것을 알아보았으니 이제 실제 프로그램 내에서 저 구조체를 어떻게 사용하는지 알아보도록 하겠습니다. 아래처럼
struct 구조체명 변수명;
정의한 구조체를 사용하려면 그 구조체를 변수로 선언해야 합니다. 그러기 위해서 뒤에 변수명을 붙여 구조체의 변수로 선언해주는 작업이 필요합니다. 예를 들어보면
struct score
{
int scoreKOR;
int scoreMAT;
int scoreENG;
int scoreSCI;
}
void main()
{
struct score a;
}
구조체 score를 정의하였고, 메인 함수 안에 그 구조체의 변수 a를 선언하였습니다. 이 a를 가지고 구조체 안의 변수에 접근하여 값을 읽고 쓸 수 있게 됩니다.
구조체 변수를 선언할때 초기화도 가능합니다.
struct score a = ;
일반 배열의 초기화처럼 중괄호로 묶고 각각의 원소를 콤마로 구분해 주었습니다. 구조체 내에 선언된 변수 순서대로 각각 초기화가 되게 됩니다.
그럼 a에 직접 값을 넣어봅시다.
a.scoreKOR = 100;
a.scoreMAT = 100;
a.scoreENG = 80;
a.scoreSCI = 50;
이런 식으로 변수명 뒤에 '.'을 붙여 구조체 내의 변수에 접근합니다. 이렇게 접근한 변수는 일반 변수와 똑같이 사용할 수 있습니다.
구조체 변수는 배열로도 선언이 가능합니다. 맨 처음 예에서 10명의 성적을 저장하는 것을 배열로 만들어봤으니 구조체로도 만들어 봅시다.
struct score a[10];
a[0].scoreKOR = 100;
a[0].scoreMAT = 75;
a[0].scoreENG = 87;
a[0].scoreSCI = 97;
a[1].scoreKOR = 68;
a[1].scoreMAT = 42;
a[1].scoreENG = 100;
a[1].scoreSCI = 74;
...
이런 식으로 사용할 수 있습니다. 주의해야할 것은 배열의 []는 구조체 변수 뒤에 붙인다는 것입니다.
2. 구조체와 포인터
구조체 변수는 배열로 선언이 가능하니 당연히 포인터로도 됩니다.
struct score a; // 일반변수
struct score b[10]; // 배열
struct score *p; // 포인터
역시 일반 포인터의 선언과 다르지 않습니다. 위의 선언을 사용해서 대입연산을 해보겠습니다.
p = &a;
일반적인 포인터와 역시 다르지 않습니다. 일반 변수 a의 주소값을 p에 넣었고, 두번째줄에서는 배열의 이름 b는 포인터이므로 바로 p에 대입하였습니다.
이처럼 구조체 포인터 변수 역시 일반 포인터와 같은 성질과 사용법을 가졌지만, 구조체 안의 변수에 접근하는 방법이 조금 다릅니다.
p->scoreKOR = 100;
p->scoreSCI = 75;
p->scoreENG = 96;
p->scoreSCI = 75;
일반 구조체 변수는 안의 변수에 접근할때 '.'을 사용하였씁니다. 하지만 구조체 포인터 변수를 사용할때는 '.' 대신 '->'를 사용하여 접근하여야 합니다. '.'과 '->'를 구분하여 사용하시기 바랍니다.
3. 구조체 선언을 쉽게 하는 방법
구조체를 정의하고 이것을 변수로 선언하려면 반드시 다음처럼
struct score a;
앞에 'struct'를 붙여서 구조체라는 것을 알려주어야 합니다. 이런 선언이 한두번이면 상관없겠지만 횟수가 많다면 매번 'struct'를 붙여주어야 하므로 불편합니다. 이것을 일반 변수처럼 선언하고 사용하는 방법이 있습니다.
typedef struct _score
{
...
} SCORE;
void main()
{
SCORE a;
}
이런 식으로 하면 메인 함수안에서 'struct'를 붙이고 할 필요없이 일반 변수 선언처럼 간단하게 사용할 수 있는 것을 알 수 있습니다. 이것은 구조체를 정의할때 앞에 'typedef'라는 키워드를 사용했기 때문인데 이것은 '사용자 지정 타입'을 만들 수 있게 해줍니다.
typedef 자료형 이름;
이와 같이 사용하며, 여러가지 예를 들어보겠습니다.
typedef int AAA;
typedef unsigned char BBB;
typedef struct _score { ... } SCORE;
마지막줄은 위의 구조체 예를 한줄로 써본 것입니다. 이런 식으로 구조체의 선언에서 typedef를 많이 사용하니 잘 알아두시기 바랍니다.
4. 프로그램에 적용
구조체를 알아보았으니 성적관리 프로그램에도 구조체를 만들어 봅시다. 우리가 사용하는 각각의 성적들은 학생이라는 범주안에 다 들어간다고 생각할 수 있습니다. 이 학생의 구조체를 만들어보면
typedef struct _student
{
char name[20] // 이름
int scoreKOR; // 국어점수
int scoreMAT; // 수학점수
int scoreENG; // 영어점수
int scoreSCI; // 과학점수
char *comment; // 평가
} STUDENT;
STUDENT student[20];
이런 식으로 만들 수 있습니다. 지난시간에 전역 변수로 이것들을 만들었는데, 이번시간에는 구조체로 바꾸는 것입니다. 구조체를 정의한 아래에는 전역변수로 구조체 배열을 선언하였습니다. 이렇게 지난시간에 선언한 전역변수를 대신하는 구조체를 선언했으니, 전역변수는 모두 삭제해 줍니다.
다음은 각 함수들을 수정해 보겠습니다. 먼저 입력 함수부터 아래처럼 고쳐줍니다.
int inputGrade(int index)
{
char temp[1024] = "\0"; // 임시 저장
int len = 0; // 문자열 길이
do
{
printf("이름을 입력하세요 : ");
scanf_s("%s", temp, sizeof(temp));
len = strlen(temp) + 1;
if (len > sizeof(student[index].name))
printf("너무 긴 이름을 입력하셨습니다.\n");
else
strcpy_s(student[index].name, sizeof(student[index].name), temp);
} while (len > sizeof(student[index].name));
printf("국어점수를 입력하세요 : ");
scanf_s("%d", &student[index].scoreKOR);
printf("수학점수를 입력하세요 : ");
scanf_s("%d", &student[index].scoreMAT);
printf("영어점수를 입력하세요 : ");
scanf_s("%d", &student[index].scoreENG);
printf("과학점수를 입력하세요 : ");
scanf_s("%d", &student[index].scoreSCI);
getchar();
printf("평가를 입력하세요 : ");
gets_s(temp, sizeof(temp));
len = strlen(temp) + 1;
student[index].comment = (char*) malloc(len);
strcpy_s(student[index].comment, len, temp);
return 0;
}
이전 소스에서 배열로 되어 있던 각 변수들을 위에서 선언한 구조체에 맞게끔 고쳐주었습니다. 다음은 계산 함수를 수정해 봅시다.
int calcGrade(int index, int *scoreTotal, float *scoreAverage, char *grade)
{
// 총점 계산
*scoreTotal = student[index].scoreKOR + student[index].scoreMAT + student[index].scoreENG + student[index].scoreSCI;
// 평균 계산
scoreAverage = (float) *scoreTotal / 4;
// 평균값에 대응하는 등급 구하기
if (*scoreAverage >= 90)
*grade = 'A';
if (*scoreAverage >= 80)
*grade = 'B';
if (*scoreAverage >= 70)
*grade = 'C';
if (*scoreAverage >= 60)
*grade = 'D';
else
*grade = 'F';
return 0;
}
다음은 출력 함수를 고쳐봅시다.
int outputGrade(int index)
{
char grade = 0;
int scoreTotal = 0;
float scoreAverage = 0.0F;
calcGrade(index, &scoreTotal, &scoreAverage, &grade);
// 화면 출력
printf("%s\t\t", student[index].name);
printf("%c\t", grade);
printf("%d\t", student[index].scoreKOR);
printf("%d\t", student[index].scoreMAT);
printf("%d\t", student[index].scoreENG);
printf("%d\t", student[index].scoreSCI);
printf("%d\t%.1f\t", scoreTotal, scoreAverage);
printf("%s\n", student[index].comment);
return 0;
}
마지막으로 메인 함수의 뒷부분을 다음과 같이 수정합니다.
...
while(index)
{
index--;
free(student[index].comment);
}
...
여기까지 성적관리 프로그램에 구조체를 적용해 보았습니다. 다음 시간에는 여러 소스 파일로 분할하여 사용하는 방법에 대해 알아보도록 하겠습니다.
'강좌 > C 언어' 카테고리의 다른 글
초보자를 위한 기초 C 언어 강좌 #21 : 전처리문 (2) | 2021.03.05 |
---|---|
초보자를 위한 기초 C 언어 강좌 #20 : 헤더파일과 소스분할 (13) | 2017.04.14 |
초보자를 위한 기초 C 언어 강좌 #18 : 전역변수와 정적변수 (2) | 2016.09.04 |
초보자를 위한 기초 C 언어 강좌 #17 : 함수 (9) | 2016.08.18 |
초보자를 위한 기초 C 언어 강좌 #16 : 문자열 함수 (2) | 2016.08.06 |
- Total
- Today
- Yesterday