Skip to content

41. 구조체(Struct)


1. 키워드

  • 구조체(Struct)
  • 파생형(Derived Type)
  • 구조체 멤버(Member)
  • 구조체 태그(Tag)
  • 구조체 별칭(Alias)
  • 익명 구조체(Anonymous Structure)


2. 구조체 사용하기

  • 지금까지 자료형별로 변수를 하나씩 선언해서 사용했다.
  • 하지만 프로그램을 만들다 보면 변수 한두 개로는 처리하기 힘든 상황이 발생한다.


  • 만약 인적 정보를 처리한다면 다음과 같이 이름, 나이, 주소 등을 저장할 변수가 필요하다.


char name[20];     // 이름
int age;           // 나이
char address[100]; // 주소


  • name, age, address 변수에는 한 사람의 정보만 저장할 수 있다.
  • 여러 명의 정보를 저장하려면 name1, name2처럼 변수 이름을 바꿔서 계속 추가해야 한다.


char name1[20];
char name2[20];
...
char name100[20];

int age1;
int age2;
...
int age100;

char address1[100];
char address2[100];
...
char address100[100];


  • C는 자료를 체계적으로 관리하기 위해 구조체라는 문법을 제공한다.
  • 구조체는 struct 키워드로 정의하며 Data Structure의 약어로 struct를 사용한다.


  • 다음은 인적 정보를 표현한 구조체인데, struct PersonPerson 구조체를 정의한다는 뜻이다.


struct Person {
    char name[20];     // 이름
    int age;           // 나이
    char address[100]; // 주소
};


  • 이름, 나이, 주소 정보가 알아보기 쉽게 Person이라는 구조체 안에 들어있다.
  • 이제 구조체로 이름, 나이, 주소를 따로 처리하지 않고 사람 단위로 정보를 처리할 수 있다.


001


  • Person 구조체를 사용하여 변수를 만들어내면 인적 정보를 손쉽게 추가할 수 있고, 구조체 자체도 배열로 만들면 10명이든 100명이든 인적 정보를 체계적으로 관리할 수 있다.


char name1[20];
char name2[20];
...
char name100[20];

int age1;
int age2;
...
int age100;

char address1[100];
char address2[100];
...
char address100[100];
//    ↑ struct Person people[100];


  • 구조체는 관련 정보를 하나의 의미로 묶을 때 사용한다.
  • 특히 목적에 맞는 자료형을 만들어서 사용하는데, 기본 자료형을 조합하여 만든 자료형을 파생형이라고 한다.


3. 구조체를 만들고 사용하기

  • 구조체는 struct 키워드로 정의한다.


struct 구조체이름 {
    자료형 멤버이름;
};


  • 구조체는 정의만 해서는 사용할 수가 없다.
  • 따라서 구조체도 변수로 선언해서 사용한다.


struct 구조체이름 변수이름;


  • 이제 인적 정보를 표현하는 구조체를 만들어보자.


#define _CRT_SECURE_NO_WARNINGS // strcpy 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h> // strcpy 함수가 선언된 헤더 파일

struct Person // 구조체 정의
{
    char name[20];     // 구조체 멤버 1
    int age;           // 구조체 멤버 2
    char address[100]; // 구조체 멤버 3
};

int main()
{
    struct Person p1; // 구조체 변수 선언

    // 점으로 구조체 멤버에 접근하여 값 할당
    strcpy(p1.name, "홍길동");
    p1.age = 30;
    strcpy(p1.address, "서울시 용산구 한남동");

    // 점으로 구조체 멤버에 접근하여 값 출력
    printf("이름: %s\n", p1.name);
    printf("나이: %d\n", p1.age);
    printf("주소: %s\n", p1.address);

    return 0;
}

// 이름: 홍길동
// 나이: 30
// 주소: 서울시 용산구 한남동


  • 먼저 struct 키워드 뒤에 구조체 이름을 지정해 주고 {}(중괄호) 안에 변수를 선언한다.
  • 이렇게 구조체 안에 들어있는 변수를 멤버라고 한다.
  • 그리고 구조체를 정의할 때 }(닫는 중괄호) 뒤에는 반드시 ;(세미콜론)을 붙여준다.


struct Person // 구조체 정의
{
    char name[20];     // 구조체 멤버 1
    int age;           // 구조체 멤버 2
    char address[100]; // 구조체 멤버 3
};


  • 구조체는 보통 main 함수 바깥에 정의한다.
  • 만약 함수 안에 구조체를 정의하면 해당 함수 안에서만 구조체를 사용할 수 있다.


  • 정의한 구조체를 사용하려면 구조체 변수를 선언해야 한다.
  • 이때 구조체 이름 앞에 반드시 struct 키워드를 붙여준다.
  • 다음은 Person 구조체 타입의 변수 p1을 선언한다는 뜻이다.


struct Person p1; // 구조체 변수 선언


  • 이제 구조체 멤버에 접근해 보자.
  • 다음과 같이 일반 변수로 선언한 구조체의 멤버에 접근할 때는 .(점)을 사용한다.


// 점으로 구조체 멤버에 접근하여 값 할당
strcpy(p1.name, "홍길동");
p1.age = 30;
strcpy(p1.address, "서울시 용산구 한남동");

// 점으로 구조체 멤버에 접근하여 값 출력
printf("이름: %s\n", p1.name);
printf("나이: %d\n", p1.age);
printf("주소: %s\n", p1.address);


  • p1.age = 30;과 같이 구조체 멤버에 접근한 뒤 값을 할당하고, p1.age와 같이 값을 가져온다.
  • p1.name 등의 문자열 멤버는 =(할당 연산자)로 저장할 수 없으므로 strcpy 함수를 사용하면 된다.


  • 지금까지 구조체 정의와 선언을 따로 했다.
  • }; 사이에 변수를 지정해 주면 구조체를 정의하는 동시에 변수를 선언할 수 있다.


struct 구조체이름 {
    자료형 멤버이름;
} 변수;
#define _CRT_SECURE_NO_WARNINGS // strcpy 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h> // strcpy 함수가 선언된 헤더 파일

struct Person // 구조체 정의
{
    char name[20];     // 구조체 멤버 1
    int age;           // 구조체 멤버 2
    char address[100]; // 구조체 멤버 3
} p1;                  // 구조체를 정의하는 동시에 변수 p1 선언

int main()
{
    // 점으로 구조체 멤버에 접근하여 값 할당
    strcpy(p1.name, "홍길동");
    p1.age = 30;
    strcpy(p1.address, "서울시 용산구 한남동");

    // 점으로 구조체 멤버에 접근하여 값 출력
    printf("이름: %s\n", p1.name);
    printf("나이: %d\n", p1.age);
    printf("주소: %s\n", p1.address);

    return 0;
}

// 이름: 홍길동
// 나이: 30
// 주소: 서울시 용산구 한남동


  • struct 키워드로 Person 구조체를 정의하면서 }; 사이에 변수 p1을 지정했다.
  • 이렇게 하면 Person 구조체를 정의하는 동시에 변수 p1이 선언된다.


struct Person // 구조체 정의
{
    char name[20];     // 구조체 멤버 1
    int age;           // 구조체 멤버 2
    char address[100]; // 구조체 멤버 3
} p1;                  // 구조체를 정의하는 동시에 변수 p1 선언


  • 구조체 멤버에 접근하여 값을 할당하고 가져오는 방법은 앞과 같다.


p1은 전역 변수

  • 자세히 보면 구조체 변수 p1main 함수 바깥에 선언되어 있다.
  • 이렇게 어떤 함수에도 속해 있지 않은 변수를 전역 변수라고 한다.


구조체 변수를 선언하는 동시에 초기화하기

  • 구조체 변수를 선언하는 동시에 값을 초기화하려면 중괄호 안에 .과 멤버 이름을 적고 값을 할당한다.


struct 구조체이름 변수이름 = {.멤버이름1 = 값1, .멤버이름2 = 값2};


  • 멤버 이름과 = 없이 값만 ,로 구분하여 나열해도 된다.
  • 단, 이때는 구조체 멤버가 선언된 순서대로 넣고, 각 멤버의 자료형에 맞게 넣는다.
  • 즉, 처음부터 순서대로 값을 채워 넣어야 하며 중간에 있는 멤버만 값을 할당하거나, 중간에 있는 멤버만 생략할 수는 없다.


struct 구조체이름 변수이름 = {값1, 값2};
#include <stdio.h>

struct Person
{
    char name[20];
    int age;
    char address[100];
};

int main()
{
    // name에는 "홍길동", age에는 30, address에는 "서울시 용산구 한남동"
    struct Person p1 = {.name = "홍길동", .age = 30, .address = "서울시 용산구 한남동"};

    printf("이름: %s\n", p1.name);
    printf("나이: %d\n", p1.age);
    printf("주소: %s\n", p1.address);

    // name에는 "고길동", age에는 40, address에는 "서울시 서초구 반포동"
    struct Person p2 = {"고길동", 40, "서울시 서초구 반포동"};

    printf("이름: %s\n", p2.name);
    printf("나이: %d\n", p2.age);
    printf("주소: %s\n", p2.address);

    return 0;
}

// 이름: 홍길동
// 나이: 30
// 주소: 서울시 용산구 한남동
// 이름: 고길동
// 나이: 40
// 주소: 서울시 서초구 반포동


4. typedefstruct 키워드 없이 구조체 선언하기

  • struct 키워드를 생략하고 싶을 때는 typedef로 구조체를 정의하면서 별칭을 지정해 주면 된다.


#define _CRT_SECURE_NO_WARNINGS // strcpy 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h> // strcpy 함수가 선언된 헤더 파일

typedef struct _Person // 구조체 이름은 _Person
{
    char name[20];     // 구조체 멤버 1
    int age;           // 구조체 멤버 2
    char address[100]; // 구조체 멤버 3
} Person;              // typedef를 사용하여 구조체 별칭을 Person으로 정의

int main()
{
    Person p1; // 구조체 별칭 Person으로 변수 선언

    // 점으로 구조체 멤버에 접근하여 값 할당
    strcpy(p1.name, "홍길동");
    p1.age = 30;
    strcpy(p1.address, "서울시 용산구 한남동");

    // 점으로 구조체 멤버에 접근하여 값 출력
    printf("이름: %s\n", p1.name);
    printf("나이: %d\n", p1.age);
    printf("주소: %s\n", p1.address);

    return 0;
}

// 이름: 홍길동
// 나이: 30
// 주소: 서울시 용산구 한남동


  • 구조체를 정의할 때 맨 앞에 typedef를 붙여주고 구조체를 정의한다.
  • 그리고 }; 사이에 구조체 별칭을 지정하면 된다.
  • 다음은 _Person이라는 구조체를 정의하면서 구조체 별칭은 Person으로 하겠다는 뜻이다.


typedef struct _Person // 구조체 이름은 _Person
{
    char name[20];     // 구조체 멤버 1
    int age;           // 구조체 멤버 2
    char address[100]; // 구조체 멤버 3
} Person;              // typedef를 사용하여 구조체 별칭을 Person으로 정의


  • 이제 구조체를 사용하려면 변수를 선언해야 한다.
  • 구조체 별칭을 정의했으므로 struct 키워드는 생략하고 구조체 별칭으로 바로 변수를 선언하면 된다.


Person p1; // 구조체 별칭 Person으로 변수 선언


  • 구조체 별칭으로 선언한 변수도 구조체 멤버에 접근할 때는 .을 사용한다.


// 점으로 구조체 멤버에 접근하여 값 할당
strcpy(p1.name, "홍길동");
p1.age = 30;
strcpy(p1.address, "서울시 용산구 한남동");

// 점으로 구조체 멤버에 접근하여 값 출력
printf("이름: %s\n", p1.name);    // 이름: 홍길동
printf("나이: %d\n", p1.age);     // 나이: 30
printf("주소: %s\n", p1.address); // 주소: 서울시 용산구 한남동


  • 만약 구조체 별칭을 사용하지 않고 구조체 이름으로 변수를 선언하려면, 다음과 같이 struct 키워드에 구조체 이름으로 변수를 선언하면 된다.


struct _Person p1; // 구조체 이름으로 변수 선언


  • 즉, struct _Person p1;Person p1은 완전히 같다.


typedef 활용하기

  • typedef는 자료형의 별칭을 만드는 기능이다.
  • 따라서 구조체뿐만 아니라 모든 자료형의 별칭을 만들 수 있다.


typedef 자료형 별칭
typedef 자료형 *별칭


  • 다음은 int 타입을 별칭 MYINT, int 타입 포인터를 별칭 PMYINT로 정의하는 예제이다.
  • 보통 포인터 별칭은 포인터라는 의미로 앞에 P를 붙인다.
  • 별칭으로 변수와 포인터 변수를 선언한다는 점만 다를 뿐 사용 방법은 일반 변수, 포인터와 같다.


typedef int MYINT;   // int 타입을 별칭 MYINT로 정의
typedef int *PMYINT; // int 타입 포인터를 별칭 PMYINT로 정의

MYINT num1;     // MYINT로 변수 선언
PMYINT numPtr1; // PMYINT로 포인터 변수 선언

numPtr1 = &num1; // 포인터에 변수의 주소 저장
                 // 사용 방법은 일반 변수, 포인터와 같음


  • 이처럼 typedef로 정의한 별칭을 사용자 정의 자료형, 사용자 정의 타입이라 부른다.


  • 여기서 PMYINT는 안에 *가 이미 포함되어 있으므로 포인터 변수를 선언할 때 *를 붙여버리면 이중 포인터가 되므로 사용에 주의해야 한다.


PMYINT *numPtr1; // PMYINT에는 *가 이미 포함되어 있어서 이중 포인터가 선언됨
int **numPtr2;   // PMYINT *와 같은 의미. 이중 포인터


구조체 태그

  • struct 뒤에 붙는 구조체 이름은 원래 구조체 태그라 부른다.
  • 그리고 typedef로 정의한 구조체 별칭은 사용자 정의 타입의 이름이라 할 수 있다.


struct 태그 {
    자료형 멤버이름;
};

typedef struct 태그 {
    자료형 멤버이름;
} 타입이름;


  • C는 나온지가 오래된 언어이다 보니 여러 가지 관습이 남아 있는데, 구조체 태그와 타입 이름을 구분하기 위해 관례상 태그 앞에 _, tag_, tag를 붙이고 있다.
  • 코드에 따라서 태그 뒤에 _t를 붙이기도 한다.


_Person
tag_Person
tagPerson
Person_t


  • 구조체 태그와 타입 이름은 사실상 같은 구조체를 지칭하므로 이름을 완전히 다르게 지을 필요는 없다.


typedef struct Person // 구조체 이름은 Person
{
    char name[20];     // 구조체 멤버 1
    int age;           // 구조체 멤버 2
    char address[100]; // 구조체 멤버 3
} Person;              // typedef로 정의한 타입 이름도 Person


  • 이때는 구조체 변수를 struct Person p1;과 같이 선언해도 되고, Person p1;과 같이 선언해도 된다.


5. 익명 구조체 사용하기

  • 익명 구조체를 사용하면 구조체 이름을 지정하지 않아도 된다.
  • 즉, typedef로 구조체를 정의하면서 이름을 생략할 수 있다.


typedef struct {
    자료형 멤버이름;
} 구조체별칭;


  • 변수는 구조체 별칭으로 선언하면 된다.


구조체별칭 변수이름;


  • 다음 내용을 입력한 뒤 실행해 보자.


#define _CRT_SECURE_NO_WARNINGS // strcpy 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h> // strcpy 함수가 선언된 헤더 파일

typedef struct // 구조체 이름이 없는 익명 구조체
{
    char name[20];     // 구조체 멤버 1
    int age;           // 구조체 멤버 2
    char address[100]; // 구조체 멤버 3
} Person;              // typedef를 사용하여 구조체 별칭을 Person으로 정의

int main()
{
    Person p1; // 구조체 별칭 Person으로 변수 선언

    // 점으로 구조체 멤버에 접근하여 값 할당
    strcpy(p1.name, "홍길동");
    p1.age = 30;
    strcpy(p1.address, "서울시 용산구 한남동");

    // 점으로 구조체 멤버에 접근하여 값 출력
    printf("이름: %s\n", p1.name);
    printf("나이: %d\n", p1.age);
    printf("주소: %s\n", p1.address);

    return 0;
}

// 이름: 홍길동
// 나이: 30
// 주소: 서울시 용산구 한남동


  • typedef struct 뒤에 이름을 지정하지 않고 바로 {를 시작하면 된다.
  • 단, 이때는 반드시 구조체 별칭을 지정해 줘야 한다.


typedef struct // 구조체 이름이 없는 익명 구조체
{
    char name[20];     // 구조체 멤버 1
    int age;           // 구조체 멤버 2
    char address[100]; // 구조체 멤버 3
} Person;              // typedef를 사용하여 구조체 별칭을 Person으로 정의


  • 구조체 변수는 Person p1;과 같이 구조체 별칭으로 선언하고, 멤버에 접근할 때는 .으로 접근하면 된다.

References