41. 구조체(Struct)
1. 키워드
- 구조체(Struct)
- 파생형(Derived Type)
- 구조체 멤버(Member)
- 구조체 태그(Tag)
- 구조체 별칭(Alias)
- 익명 구조체(Anonymous Structure)
2. 구조체 사용하기
- 지금까지 자료형별로 변수를 하나씩 선언해서 사용했다.
- 하지만 프로그램을 만들다 보면 변수 한두 개로는 처리하기 힘든 상황이 발생한다.
- 만약 인적 정보를 처리한다면 다음과 같이 이름, 나이, 주소 등을 저장할 변수가 필요하다.
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 Person
은Person
구조체를 정의한다는 뜻이다.
- 이름, 나이, 주소 정보가 알아보기 쉽게
Person
이라는 구조체 안에 들어있다. - 이제 구조체로 이름, 나이, 주소를 따로 처리하지 않고 사람 단위로 정보를 처리할 수 있다.
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
키워드로 정의한다.
- 구조체는 정의만 해서는 사용할 수가 없다.
- 따라서 구조체도 변수로 선언해서 사용한다.
- 이제 인적 정보를 표현하는 구조체를 만들어보자.
#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
을 선언한다는 뜻이다.
- 이제 구조체 멤버에 접근해 보자.
- 다음과 같이 일반 변수로 선언한 구조체의 멤버에 접근할 때는
.
(점)을 사용한다.
// 점으로 구조체 멤버에 접근하여 값 할당
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
함수를 사용하면 된다.
- 지금까지 구조체 정의와 선언을 따로 했다.
}
와;
사이에 변수를 지정해 주면 구조체를 정의하는 동시에 변수를 선언할 수 있다.
#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
은 전역 변수
- 자세히 보면 구조체 변수
p1
은main
함수 바깥에 선언되어 있다. - 이렇게 어떤 함수에도 속해 있지 않은 변수를 전역 변수라고 한다.
구조체 변수를 선언하는 동시에 초기화하기
- 구조체 변수를 선언하는 동시에 값을 초기화하려면 중괄호 안에
.
과 멤버 이름을 적고 값을 할당한다.
- 멤버 이름과
=
없이 값만,
로 구분하여 나열해도 된다. - 단, 이때는 구조체 멤버가 선언된 순서대로 넣고, 각 멤버의 자료형에 맞게 넣는다.
- 즉, 처음부터 순서대로 값을 채워 넣어야 하며 중간에 있는 멤버만 값을 할당하거나, 중간에 있는 멤버만 생략할 수는 없다.
#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. typedef
로 struct
키워드 없이 구조체 선언하기
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
키워드는 생략하고 구조체 별칭으로 바로 변수를 선언하면 된다.
- 구조체 별칭으로 선언한 변수도 구조체 멤버에 접근할 때는
.
을 사용한다.
// 점으로 구조체 멤버에 접근하여 값 할당
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;
과Person p1
은 완전히 같다.
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
는 안에*
가 이미 포함되어 있으므로 포인터 변수를 선언할 때*
를 붙여버리면 이중 포인터가 되므로 사용에 주의해야 한다.
구조체 태그
struct
뒤에 붙는 구조체 이름은 원래 구조체 태그라 부른다.- 그리고
typedef
로 정의한 구조체 별칭은 사용자 정의 타입의 이름이라 할 수 있다.
- C는 나온지가 오래된 언어이다 보니 여러 가지 관습이 남아 있는데, 구조체 태그와 타입 이름을 구분하기 위해 관례상 태그 앞에
_
,tag_
,tag
를 붙이고 있다. - 코드에 따라서 태그 뒤에
_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
로 구조체를 정의하면서 이름을 생략할 수 있다.
- 변수는 구조체 별칭으로 선언하면 된다.
- 다음 내용을 입력한 뒤 실행해 보자.
#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;
과 같이 구조체 별칭으로 선언하고, 멤버에 접근할 때는.
으로 접근하면 된다.