1. 구조체(Structure Types)


이번 편에서는 구조체(Structure Types)에 대해서 알려드리려고 합니다. 서로 다른 변수의 형태를 하나의 블럭으로 묶은걸 구조체라고 하며 구조체의 선언방법은 아래와 같습니다.

struct 구조체의 이름 {
  멤버 변수;
};

여기서 멤버 변수는 구조체 안에서 정의된 변수를 의미하며 우리가 일반적으로 변수를 선언하는 방식과 다르지 않습니다. 필드라고도 하고 구조체 원소라고 부르기도 합니다. 그러면 이제 우리가 구조체를 직접 선언해볼까요?

#include <stdio.h>

struct student {
  int id;
  char *name;
  float percentage;
}; // 구조체 뒤에 세미콜론이 와야함
 
int main() {
  struct student s;
  s.id=1;
  s.name = "김철수";
  s.percentage = 90.5;
  printf("아이디: %d \n", s.id);
  printf("이름: %s \n", s.name);
  printf("백분율: %f \n", s.percentage);
  return 0;
}
결과:

아이디: 1
이름: 김철수
백분율: 90.500000
계속하려면 아무 키나 누르십시오 . . .


3~7행을 보시면 구조체 student를 정의하였고 10행을 보시면 구조체 변수 s를 선언하였습니다. 11~13행을 보시면 구조체의 접근 방법을 보여주며 구조체 변수 s의 각각의 멤버 변수에 값을 저장하고 있습니다. 구조체를 사용하면 관련이 있는 정보끼리 한꺼번에 묶을수 있다는 장점이 있습니다. 11~13행을 다시 보시면 . 연산자로 구조체의 멤버를 참조할수 있습니다. 구조체는 어떻게 초기화 할까요?

struct student s={1,"김철수",90.5};

보시는 바와 같이 중괄호를 사용하여 초기값을 적습니다. 그런데, 일일히 구조체를 선언할때 struct를 붙이고 구조체를 선언하다 보니, 여간 귀찮은게 아닙니다. 이는 typedef라는 키워드로 간단하게 줄일 수 있습니다.

/* cprogramminglanguage.net */
#include <stdio.h>

typedef struct _student{
    char name[50];
    unsigned int mark;
} student;
 
void print_list(student list[], int size);
void read_list(student list[], int size);
 
int main(){
 
    const int size = 2;
    student list[size];
    
    read_list(list,size);
 
    print_list(list,size);
}
void read_list(student list[], int size)
{
    printf("학생의 정보를 입력하여 주세요:\n");
 
    for(int i = 0; i < size;i++){
        printf("\n이름:");
        scanf("%s",&list[i].name);
 
        printf("\n마크:");
        scanf("%u",&list[i].mark);
    }
}
void print_list(student list[], int size){
    printf("학생들의 정보:\n");
    
    for(int i = 0; i < size;i++){
        printf("\n이름: %s, 마크: %u",list[i].name,list[i].mark);
    }
}

결과:

학생의 정보를 입력하여 주세요:

이름:김철수

마크:4

이름:김영희

마크:5

학생들의 정보:

이름: 김철수, 마크: 4
이름: 김영희, 마크: 5
계속하려면 아무 키나 누르십시오 . . .


이 예제의 이해가 살짝 힘들겠지만, 차근차근 살펴보도록 합시다. 우선, 4행에 등장한 typedef 키워드는 잠시 제껴두고 9~10행을 보시면 함수의 매개변수란에 구조체 배열을 인자로 받을 수 있게끔 해놓았습니다. 여기서, 구조체도 역시 배열로 선언할 수 있으며 함수로 전달할 수 있는 녀석임을 알 수 있습니다. 이어서, typedef에 대한 설명을 마저 하도록 하겠습니다.

typedef 데이터타입 이름;

이 typedef란 녀석은, 기존 데이터 타입에 새로운 이름을 부여합니다. 말 그대로 원래 있던 데이터 타입에 별명을 붙인다는 소리와 같습니다. 한번 직접 사용해볼까요?

#include <stdio.h>

typedef int INT_VAR;
int main()
{
    INT_VAR a=50;
    INT_VAR b=40;
    
    printf("a와 b를 더한 결과: %d\n", a + b);
    return 0;
}

결과:

a와 b를 더한 결과: 90
계속하려면 아무 키나 누르십시오 . . .


typedef 키워드를 사용하여 int에 INT_VAR라는 새로운 이름을 붙여주었습니다. int와 INT_VAR는 같은 녀석이라는 소리입니다. 그리고 INT_VAR(int)로 a, b를 선언하고 값을 각각 50과 40으로 초기화 후에 더한 후의 결과물을 출력했습니다. 그렇다면 다시 위 코드로 돌아가 15행을 볼까요?

student list[size];

이는 구조체의 배열 선언으로 배열의 선언방식과 다르지 않고, 이 구조체 배열은 구조체 변수의 모임입니다. 따로 설명드리지 않아도 이해하실것 같아서 넘어가겠습니다. 17행과 22행을 봐볼까요? 구조체 배열 list를 첫번째 인자로 받고있으며 두번째는 그 배열의 길이를 인자로 받고 있습니다. 우리가 10편에서 배웠던 매개변수의 선언방식과 동일합니다. 이제는 한번 구조체 포인터로 들어가볼까요?

2. 구조체 포인터


전 파트의 '포인터'만 잘 이해하셨다면, 이번 구조체 포인터 파트도 별로 어려울 부분이 없습니다. 구조체 포인터는 어떻게 쓰는지 한번 보도록 합시다.

/* www.roseindia.net/c-tutorials/c-structure-pointer.shtml */
#include <stdio.h>

int main() {
  struct st {
  int id;
  char *name;
  char *address;
  };
  struct st employee, *stptr;
  stptr = &employee;
  stptr->id = 1;
  stptr->name = "홍길동";
  stptr->address ="서울";
  printf("직원 안내: 아이디=%d\n%s\n%s\n", stptr->id, stptr->name,
  stptr->address);
  return 0;
}
결과:
직원 안내: 아이디=1
홍길동
서울
계속하려면 아무 키나 누르십시오 . . .


10행을 볼까요? 구조체 변수 employee와 구조체 포인터 stptr가 선언되었으며 stptr에 employee의 주소를 기억시키고 있습니다. 그리고 이 구조체 포인터 stptr를 사용하여 구조체 employee의 멤버에 접근이 가능합니다. (12행~15행) 한가지 특이한 것은 -> 연산자를 사용하여 구조체 포인터로 구조체 멤버를 참조하고 있습니다. 원래는 * 연산자를 사용하여 접근할 수 있습니다. (*stptr).id, (*stptr).name, (*stptr).address와 같이 말입니다. 그런데, 매번 구조체 포인터를 통해 멤버에 접근을 하려고 할때 연산자의 우선순위 때문에 괄호로 덮어주어야 하는 불편함이 생기기 때문에 C언어에서는 ->라는 녀석이 존재합니다. 간단하죠?