구조체(Struct)는 다양한 데이터 타입들을 하나로 묶어서 새로운 데이터 타입을 정의할 수 있는 기능을 제공합니다. 이를 통해 복잡한 데이터를 단순화하여 표현하거나, 하나의 변수로 여러 개의 데이터를 다룰 수 있게 됩니다.
구조체는 다음과 같은 형태로 정의됩니다.
struct 구조체_이름 {
데이터_타입 멤버_변수_이름1;
데이터_타입 멤버_변수_이름2;
...
};

구조체의 멤버 변수는 일반적인 변수와 마찬가지로 데이터 타입과 이름을 가지며, 세미콜론으로 끝나야 합니다. 멤버 변수의 이름은 다른 변수들과 동일하게 변수명 규칙을 따릅니다.
구조체는 변수를 선언하는 방식과 유사합니다. 구조체 변수를 선언하고 초기화하는 방법은 다음과 같습니다.
struct 구조체_이름 구조체_변수_이름 = {멤버_변수_값1, 멤버_변수_값2, ...};
또는
struct 구조체_이름 구조체_변수_이름;
구조체_변수_이름.멤버_변수_이름1 = 멤버_변수_값1;
구조체_변수_이름.멤버_변수_이름2 = 멤버_변수_값2;
...
구조체 변수에 접근하기 위해서는 점(.) 연산자를 사용합니다. 예를 들어, 구조체변수이름.멤버변수이름 형태로 멤버 변수의 값을 가져올 수 있습니다.
구조체는 다양한 용도로 활용됩니다. 예를 들어, 구조체를 사용하여 학생 데이터를 저장할 수 있습니다. 이 경우, 구조체의 멤버 변수로는 학생의 이름, 나이, 성적 등이 포함될 수 있습니다. 이렇게 구조체를 활용하면 복잡한 데이터를 더 쉽게 다룰 수 있습니다.
구조체를 알아보았으니 성적관리 프로그램에도 구조체를 만들어 봅시다. 우리가 사용하는 각각의 성적들은 학생이라는 범주안에 다 들어간다고 생각할 수 있습니다. 이 학생의 구조체를 만들어보면
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 언어 프로그래밍에 있어서 중요한 부분 중 하나라고 생각합니다.