Media Log


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언어에서는 ->라는 녀석이 존재합니다. 간단하죠?


저작자 표시
신고
  1. 김승현 at 2012.09.30 10:19 신고 [edit/del]

    감사합니다~

    Reply
  2. Mstd at 2014.01.24 17:17 신고 [edit/del]

    #include <stdio.h>

    int main() {
    struct st {
    int id;
    char *name;
    char *address;
    } employee, *stptr;
    stptr = &employee;
    employee.id = 3;
    stptr->name = "홍길동";
    stptr->address = "서울";
    printf("직원 안내: 아이디=%d\n%s\n%s\n", stptr->id, stptr->name,
    stptr->address);
    return 0;
    }

    맨 아래의 코드와 이코드는 차이가있나요>?

    Reply
    • BlogIcon EXYNOA at 2014.01.31 13:52 신고 [edit/del]

      Mstd님이 올려주신 경우는, 구조체 st의 정의와 함께 구조체 변수인 employee와 구조체 포인터 *stptr가 정의되는 경우입니다. 이 경우에는 구조체를 따로 선언하여 사용하는 일이 없다면 구조체의 이름을 생략할 수 있습니다.

  3. 9reenw1ch at 2014.06.07 08:51 신고 [edit/del]

    INT_VAR 이라는 함수가 존재하는 건가요?
    더한다는 그런 기호도 없는데 어떻게 서로 더하죠??

    Reply
    • BlogIcon EXYNOA at 2014.06.07 10:42 신고 [edit/del]

      예제에 a + b가 아닌 a, b로 되어있음을 확인하고 수정했습니다. 그리고 본문 내용을 확인해주시기 바랍니다. 'typedef란 녀석은, 기존 데이터 타입에 새로운 이름을 부여합니다. 말 그대로 원래 있던 데이터 타입에 별명을 붙인다는 소리와 같습니다.', 'typedef 키워드를 사용하여 int에 INT_VAR라는 새로운 이름을 붙여주었습니다. int와 INT_VAR는 같은 녀석이라는 소리입니다.' 이 부분을 주목해주세요.

  4. ForSoftware at 2014.08.15 15:20 신고 [edit/del]

    안녕하세요 항상 좋은자료 업데이트에 감사드립니다. 다름이 아니라 아래의 코드에서 안되는 부분이 있어서 문의드립니다.

    #include <stdio.h>

    typedef struct student
    {
    char name[50];
    int Id;

    } student;

    void read_list(student list[],int num );
    void print_list(student list[], int num);


    int main()
    {
    int num;
    printf("학생수를 입력하세요\n");
    scanf_s("%d",&num);
    student list[sizeof(num)];
    read_list(list,num);
    print_list(list, num);
    }

    void read_list(student list[],int num)
    {
    for (int i = 0; i < num; i++)
    {

    printf("학생 이름 입력:\n");
    scanf_s("%s", &list[i].name ); //이부분에서 동작이 멈춥니다.

    printf("학번입력:\n");
    scanf_s("%d", &list[i].Id);
    }

    }

    void print_list(student list[],int num)
    {
    for (int i = 0; i < num;i++)
    {
    printf("학생이름 %s",list[i].name);
    printf("학번 : %d\n", list[i].Id);

    }

    }

    void read_list(student list[],int num) 함수 안에있는 scanf_s("%s", &list[i].name ) 부분에서 값을 못전달 하는것같습니다. ㅠ 무엇이 잘못됬는지 알려주시면 감사하겠습니다. ㅠ

    Reply
    • 지나는과객 at 2016.10.29 03:26 신고 [edit/del]

      prototype(원형) :
      scanf_s("제어문자", 변수, 변수길이)

      예 :
      scanf_s("%s", &inPut[i].name, sizeof(inPut[i].name));

      scanf_s() 의 사용법을 다시 공부하시길

  5. 학생1 at 2017.01.02 22:39 신고 [edit/del]

    #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;
    }


    이 소스의 구조체 맴버변수 char *name의 경우 포인터 변수이므로 주소를 저장하는 변수 아닌가요

    main 함수에서 구조체 변수 s를 통해 구조체 멤버변수에 접근하여 초기화를 진행할때 s.id=1; 는 int형 변수의 초기화이고 s.name="김철수" 는 포인터변수 초기화인데 보통 포인터 변수는 char *name =NULL 과 같은 형식으로 초기화를 하게 되는걸로 알고있습니다. 주소를 저장하니까요. 하지만 직접 빼고 컴파일 해본결과 에러가 나더군요. 이유를 모르겠습니다. 그냥 이렇게 쓰는건가보다 하고 넘어가기엔 찜찜해서요..

    1. 어째서 char name이 아닌 char *name 인가요
    오래된 자료라 지나가시는 분이라도 알고 계시면 답변좀 부탁드립니다.

    Reply
    • BlogIcon EXYNOA at 2017.01.03 00:18 신고 [edit/del]

      문자열 상수같은 경우는 값을 읽기만 하고 변경할 수 없는(read-only-data) 영역인 .rdata 섹션(Visual C++의 경우는 .rdata 섹션에, GCC는 .rodata 섹션)에 저장됩니다. 이 섹션은 초기화, 읽기 속성을 지니며 문자열 상수나 const로 선언된 변수처럼 읽기만 가능한 읽기 전용 데이터 섹션입니다.

      struct student s;
      s.name = "string literal";
      ..
      s.name = "new string literal";
      http://i.imgur.com/0SeSrQk.png

      코드에서 문자열 상수는 해당 문자열의 시작 번지를 가리키는 char형 포인터 상수로 처리합니다. printf 함수와 같이 char*를 인수로 요구하는 함수에서 문자열 상수를 바로 사용할 수 있는 것도 이와 같은 이유입니다.

      온라인 IDE에서 정상적으로 결과가 출력됨을 확인했으며, Visual Studio 2015 환경에서 제대로 결과가 출력됨을 확인했습니다.
      http://ideone.com/WeUEN2

    • 학생1 at 2017.01.03 00:48 신고 [edit/del]

      그러니까 구조체 멤버변수인 char *name은 어떤 문자열 상수를 가리킬 멤버변수이고 해당 문자열 상수의 주소를 가리키게 되는거군요. 그럼 "김철수"라는 문자열 상수의 시작주소를 가리키는 거겠구요.

      int printf(
      const char *format [,
      argument]...
      );

      그렇네요... 아무 생각없이 쓰던 printf함수였는데 바로 알아갑니다^^ 강좌 잘 보고 있습니다. 빠른 피드백 감사해요~

    • BlogIcon EXYNOA at 2017.01.03 01:09 신고 [edit/del]

      글을 읽다보면 미흡한 부분이 많이 있습니다. 방문자 분들이 읽다가 의혹이 생기는 부분을 댓글로 남겨주시는데, 그 부분에서 저 역시도 많은 도움을 얻어가고 있습니다.

      요즘은 다른 일에 치여서 예전처럼 빠르게 답글은 못 달아드리지만 이렇게 남겨주시면 오해의 여지를 부를 수 있는 부분은 제거하고, 미처 설명을 빠트리거나 제가 오개념을 가지고 있는 경우에는 최대한 빠르게 정정하거나 보충하려고 노력하고 있습니다. 이렇게 댓글 남겨주셔서 저야말로 감사드립니다 ^ㅡ^*

      추신. C/C++ 강좌는 3월, 4월 내로 검토 후에 전부 다 수정할 계획을 세우고 있습니다. 다른 언어 역시도 계속해서 각 버전에 따라 새롭게 추가된 부분을 업데이트해나갈 예정입니다.

  6. radu at 2017.04.04 19:42 신고 [edit/del]

    글 감사합니다!!

    Reply
  7. at 2017.05.24 17:26 신고 [edit/del]

    감사합니다!

    Reply
  8. 코딩하는너구리 at 2017.10.09 20:53 신고 [edit/del]

    감사합니다

    Reply

submit

티스토리 툴바