2023.01.13 - 안드로이드 앱개발자 과정

CHA·2023년 1월 13일
0

C언어



구조체(Struct) : 서로 연관있는 변수를 묶어서 관리하는 기법


구조체란

기존까지 우리는 변수 하나마다 하나의 데이터를 넣어 사용했습니다. 그런데 그렇게 하려니 조금 번거롭습니다. 데이터가 10개가 필요하면 10개의 변수가 필요하니까요. 그래서 배열 이라는 개념을 도입해서 데이터 10개가 든 변수 1개를 생성함으로써 번거로움을 해결했습니다.

그런데 만일, 데이터 10개가 든 변수가 10개라면요? 자 이제, 또 한번 번거로움이 느껴집니다. 그래서 이번에는 2차원 배열을 사용합니다. 그러면 데이터 10개가 든 변수가 10개라도 변수는 1개로 충분합니다.

그런데 여기서 문제가 발생합니다. 만약에 우리가 학생의 이름, 국어성적, 영어성적, 평균의 정보를 만든다고 해봅시다. 그러면 이름은 char* 로, 국어와 영어성적은 int로 평균은 double 의 자료형으로 각각 만들어주어야 합니다. 이차원 배열로 만들게 된다면 각각의 정보들을 만들 수는 있습니다.

다만, 우리가 그러한 정보를 사용할 때 굉장히 번거로워 질것같습니다. 그래서 만들어진 개념이 구조체 입니다. 구조체를 사용하면, 학생 1명의 이름과 성적,평균을 묶음으로 관리할 수 있게됩니다. 편리하죠? 그러면 먼저 구조체의 문법부터 알아봅시다.

  • 구조체의 문법

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

    다음 한 문장은 상당히 중요합니다

    구조체는 데이터를 한번에 저장할 수 있는 새로운 자료형 "설계" 이다.

    구조체 자체는 변수가 아닙니다. 구조체는 자료형 설계입니다. 그렇기 때문에 구조체를 직접적으로 사용하기 전까지는 메모리에 올라가지 않습니다. 변수가 아니니까요. 따라서 구조체를 설계할 때 초기화도 불가능하며, 연산 또한 불가합니다. 컴퓨터에게 " 나 이러한 변수들의 묶음을 만들거야 " 라고 말해주는 작업일 뿐, 실제로 변수를 만드는 작업은 아닙니다. 자, 그러면 이러한 구조체를 어떻게 사용할 수 있는지 알아봅시다.

  • 구조체의 사용

    struct Student {
        char* name;
        int kor; 
        double aver;
    };
    void main() {
    
        struct Student stu;//구조체로만든(struct) 자료형 변수명; , 메모리에 올라감.
        stu.name = "sam"; // . 연산자를 이용하여 Student 멤버에 접근
        stu.kor = 80;
        stu.aver = (stu.kor + stu.eng) / 2.0;
        
        printf("이름 : %s\n", stu.name);
    	printf("국어 : %d\n", stu.kor);
    	printf("평균 : %.2lf\n", stu.aver);
    }

    먼저, 앞서 보았던 구조체의 문법을 가지고 내가 만든 새로운 자료형의 "설계"를 합니다. 여기에서는 구조체 이름으로 Student 를 사용했으며, 멤버변수 3개를 만들었습니다. 구조체를 사용하기 위해서는 구조체를 사용하겠다 라고 선언을 해주어야 하는데, 이는 변수 선언과 동일 합니다.

    구조체 선언
    struct Student(자료형 이름) stu(구조체 변수 이름);

    그리고 나면 이 구조체가 메모리에 올라가게 됩니다. 그럼 이제 구조체 내부에 있는 멤버변수들을 사용할 수 있게됩니다. 멤버변수들의 사용은 . 연산자를 이용하여 접근합니다. 구조체는 설계도 와 같기 때문에 이 설계도는 마음껏 사용해도 됩니다. 즉, stu 변수 하나만 만들수 있는게 아니라 여러개 만들어도 상관없다는 뜻입니다. 그런데 조금 불편해집니다. 위 코드에서 구조체 변수를 여러개로 만들자니 출력문도 여러번 작성해줘야 하고 그렇게 되니 코드가 좀 껄끄럽습니다. 그러면 함수를 한번 활용해서 코드를 좀 간결하게 만들어도 괜찮겠습니다.


구조체와 함수

struct Student {
    char* name;
    int kor; 
    int eng;
    double aver;
};
void output(struct Student stu); // 함수 prototype
void main() {

    struct Student stu;
    stu.name = "sam"; 
    stu.kor = 80;
    stu.eng = 95
    stu.aver = (stu.kor + stu.eng) / 2.0;
    output(stu); // 함수호출. 호출시 파라미터로 구조체 변수 전달해줌.
    
    struct Student stu2;
    stu2.name = "robin"; 
    stu2.kor = 95;
    stu2.eng = 90;
    stu2.aver = (stu2.kor + stu2.eng) / 2.0;
    output(stu2);
}

void output(struct Student stu){ // 파라미터로 구조체 변수 전달받음.
	printf("이름 : %s\n", stu.name);
	printf("국어 : %d\n", stu.kor);
    printf("영어 : %d\n", stu.eng);
	printf("평균 : %.2lf\n", stu.aver);
}

구조체 변수 또한 변수입니다. 그렇기 때문에 기존 일반 변수와 동일하게 작동한다고 생각하면 됩니다. 기존의 일반 변수들의 자료형이 int,char 인것 처럼, 구조체 변수의 자료형은 struct Student 입니다. 그렇기 때문에 함수를 생성할 때 파라미터로 구조체 변수를 전달받을 수 있습니다. 물론 자료형은 struct Student 입니다. 역시 마찬가지로 함수를 호출할 때에도 구조체 변수를 전달해주면 됩니다. 이렇게 해서 지저분해질뻔한 코드를 조금 더 간결하게 만들 수 있었습니다.


중첩 구조체

앞서 구조체 변수 또한 일반 변수와 크게 다르지 않다고 하였습니다. 그리고 구조체의 멤버에는 변수들이 있었습니다. 그렇다면, 구조체가 다른 구조체 내부에 들어가는것도 가능하지 않을까요? 다음 코드는 구조체 내부에 다른 구조체의 변수가 들어가있는 코드 입니다.

  struct Student {
      char* name;
      int kor; 
      int eng;
      double aver;
      struct StudentClass studentClass;
  };
  struct StudentClass{
      char* classNum;
      int numbering;
  }
  void main(){
      struct Student student;
      student.name = "sam";
      student.kor = 80;
      student.eng = 70;
      student.aver = (student.kor + student.eng) / 2.0;
      student.studentClass.classNum = "1반";
      student.studentClass.numbering = 12;
  }

구조체 안에 구조체 변수를 멤버변수로 설정함에 따라, 하나의 구조체 변수로 두개의 구조체의 멤버 변수에 접근이 가능해졌습니다. 두 개, 혹은 그 이상의 구조체들이 서로 연관이 있는 구조체 라면, 그리고 적절한 포함관계를 만들어 줄 수 있다면 편리한 코드 구성이 될것같습니다.


구조체와 배열

일반 변수와 역시 마찬가지로 배열 또한 만들 수 있습니다. 코드부터 보겠습니다.

  struct Student {
      char* name;
      int kor; 
      int eng;
      double aver;
      struct StudentClass studentClass;
  };
  struct StudentClass{
      char* classNum;
      int numbering;
  }
  void main(){
      struct Student student[3];
      student[0].name = "sam";
      student[0].kor = 90;
      student[0].studentClass.classNum = "1반";
  }

구조체를 통해 한 학생의 여러가지 정보들을 묶음으로 관리할 수 있었습니다. 그리고 위 코드에서 볼 수 있듯, 학생이 여러명이라면 구조체의 배열을 통해 학생 정보를 관리할 수 있습니다. 배열과 중첩 구조체를 이용하면, 한 학년 정도의 학생들의 정보를 관리하는것에는 문제가 없을것 같습니다.


구조체와 포인터

구조체로 만든 자료형 또한 포인터로 사용도 가능합니다. 코드부터 보겠습니다.

  struct Student {
      char* name;
      int kor; 
      int eng;
      double aver;
      struct StudentClass studentClass;
  };
  struct StudentClass{
      char* classNum;
      int numbering;
  }
  void main(){
      struct Student* student;
      (*student).name = "robin";
      (*student).studentClass.classNum = "2반";
      student->kor = 90;
  }

struct Student 는 자료형이기에 기존 포인터의 문법을 가져오면, struct Student* 으로 포인터 변수의 자료형이 만들어 집니다. 하지만 연산자 우선순위에 의해서 (*student).name 처럼 변수에 접근해야 합니다. 추가적으로, 코드가 지저분하다 보니 구조체에 대한 포인터 변수의 표현인 (*student).kor = 90;을 다음과 같이 작성할 수도 있습니다. student->kor = 90;

profile
Developer

0개의 댓글