[C] 구조체
C에서 구조체란?
- 구조체는 사용자 정의 자료형이다.
- 구조체 정의 시 struct 키워드를 쓴다.
- 구조체 정의 끝에는 반드시 세미콜론(‘;’)을 쓴다.
- 구조체 멤버 접근 시 도트(‘.’)연산자를 쓴다.
- 구조체 내에 선언한 자료형들 만큼 메모리가 할당된다.
뭔가 느낌이 파이썬에서의 class랑 비슷한 느낌이 난다.
하지만 결정적 차이점이라면 파이썬에서는 self를 이용해서 선언과정에서 계산도 가능했지만
C에서는 아쉽게도 불가능하다.
구조체 선언 예시 (학생)
#include <stdio.h>
#include <string.h>
#define NAME_LEN 20 // 매크로 상수
#define PHONE_LEN 20 // 매크로 상수
struct student {
int no;
char name[NAME_LEN]; //한글은 한글자당 2바이트! 김성욱 저장하려면 최소 7바이트 필요!
char phone[PHONE_LEN]; // 용도가 다르면 값이 같더라도 매크로상수는 다르게 해주자
int score;
}; // 구조체 정의 끝에는 세미콜론을 쳐줘야한다.
이렇게 정의하면 학생이라는 자료형에 번호(no), 이름, 전화번호, 점수가 기록이 가능해진다.
이제 데이터를 저장하고 출력해보자.
void remove_enter(int len, char* in) {
// fgets 사용으로인해 입력된 \n을 \0으로 변환하기 위한 코드
in[len - 1] = '\0';
}
int main() {
int a;
struct student st1, st2; // 구조체에 대한 변수 선언
st1.no = 1;
printf("학번 : %d\n", st1.no);
printf("이름을 입력하세요 : ");
fgets(st1.name, NAME_LEN - 1, stdin);
remove_enter(strlen(st1.name), st1.name);
printf("전화번호를 입력하세요 : ");
fgets(st1.phone, PHONE_LEN - 1, stdin);
// gets_s함수는 \n 을 \0으로 바꿔주는 역할을 한다. gets_s를 쓴다면 remove_enter을 만들 필요가 없다.
remove_enter(strlen(st1.phone), st1.phone);
printf("점수를 입력하세요 : ");
scanf("%d", &st1.score);
puts("");
//학생정보 출력
printf("\n\n\t*** 학생정보 출력 ***\n\n");
printf("%4s %-20s %-20s %8s\n", "번호", "이름", "전화번호", "점수");
printf("------------------------------------\n");
printf("%4d %-20s %-20s %8d\n", st1.no, st1.name, st1.phone, st1.score);
return 0;
}
이렇게 하면 학생들에 대한 정보를 표로 출력할 수 있다.
여기서 잠깐,
void remove_enter(int len, char* in) {
// fgets 사용으로인해 입력된 \n을 \0으로 변환하기 위한 코드
in[len - 1] = '\0';
}
gets_s함수를 사용하지 못해서 fgets() 함수를 사용하고 있는데,
fgets()함수는 입력과정에서 ‘\n’ 문자까지 받아버리기 때문에 이를 없애주거나
null (‘\0’)으로 바꿔주는 작업을 해야한다. 그 기능을 하는 함수이다.
번호 이름 전화번호 점수
------------------------------------
1 우키 010-9999-6666 95
위와 같이 코드가 출력된다.
구조체 배열
그러면 구조체가 너무 많이 입력될 때에는 어떻게 해야할까?
구조체도 하나의 자료형이기 때문에 배열로 저장이 가능하다.
#include <stdio.h>
#include <stdlib.h> //system
#include <string.h>
#define NAME_LEN 20
#define PHONE_LEN 20
#define STUDENT_NUM 5 //학생수
// typedef int SIZE_t; 자료형 재정의 (자료형의 이름으로 SIZE_t를 쓸 수 있게 됨.)
// 재정의를 하는 이유 : 조금 더 명시적으로 쓰거나 사람이 자료형의 의미를 받아들이기에 편하다.
// 또한, 자료형의 이름이 긴 경우 짧게 사용하기 위해서.
typedef struct student
{
int no;
char name[NAME_LEN]; //한글은 한글자당 2바이트! 김성욱 저장하려면 최소 7바이트 필요!
char phone[PHONE_LEN]; // 용도가 다르면 값이 같더라도 매크로상수는 다르게 해주자
int score;
}student;
여기서 typedef int SIZE_t가 나오는데, 자료형의 이름을 다시 재정의하는 것이라 생각하면 된다.
즉, SIZE_t라는 자료형이 있는데 int의 한 종류로 선언이 되는 것이다.
그리고 구조체 자료형을 배열에 사용하는데 결정적인 역할을 한다.
이렇게 한 다음에
void remove_enter(int len, char* in) {
// fgets 사용으로인해 입력된 \n을 \0으로 변환하기 위한 코드
in[len - 1] = '\0';
}
int main()
{
//student st1, st2, st3, st4, st5; //구조체 변수 선언
student s[STUDENT_NUM]; //구조체 배열
int i;
//학생정보 입력
for(i=0; i<STUDENT_NUM; i++)
{
system("clear"); //화면지우기
printf("\n\n\t\t***학생정보 입력***\n\n");
s[i].no = 1111 + i;
printf("학번 : [%03d]\n", s[i].no);
printf("이름 : ");
fgets(s[i].name, NAME_LEN - 1, stdin);
remove_enter(strlen(s[i].name), s[i].name);
printf("전화번호 : ");
fgets(s[i].phone, PHONE_LEN - 1, stdin);
remove_enter(strlen(s[i].phone), s[i].phone);
printf("점수 : ");
scanf("%d", &s[i].score);
while (getchar() != '\n'); // scanf가 buffer가 \n을 가지고 있으면 넘어가는 것을 방지.
}
//학생정보 출력
system("clear");
printf("\n\n\t*** 학생정보 출력 ***\n\n");
printf("%4s %-20s %-20s %8s\n", "번호", "이름", "전화번호", "점수");
printf("------------------------------------\n");
for(i=0; i<STUDENT_NUM; i++)
{
printf("%4d %-20s %-20s %8d\n", s[i].no, s[i].name, s[i].phone, s[i].score);
}
return 0;
}
student s[배열의 길이] 형태를 만들어주고
for문을 통해서 코드를 입력해주면 구현이 가능해진다.
여기서 잠깐,
system(“clear”) : 터미널에서 자료를 입력할 때 화면을 초기화 해주는 역할을 한다.
while (getchar() != ‘n’)의 역할 : scanf 함수가 정수를 받을 때 \n을 받으며 입력과정에서 오류가 발생할 수 있는데 이를 방지한다.
%-20s 와 같은 코드 : 길이를 맞춰서 줄맞추는 용도인데 필자의 컴파일러(VSCode)는 잘 안먹힌다.. 비주얼 스튜디오 에서는 잘 먹힌다고..
이렇게 해주고 프로그램을 실행해주면
*** 학생정보 출력 ***
번호 이름 전화번호 점수
------------------------------------
1111 나나 010-5555-6666 99
1112 사랑해요 010-4444-4444 44
1113 마트 010-8888-8888 55
1114 해피해피 010-4234-1234 95
1115 호박고구마 010-3223-9999 66
위와 같이 결과가 잘 출력된다.