배열은 같은 자료형의 데이터를 묶어 처리할 수 있다.
자료형이 다른 데이터는 구조체를 통해 묶어 처리할 수 있다.
구조체는 사용자가 필요한 자료형을 만들어 사용하는 형태로 사용하기 전에 컴파일러에 미리 알려주기 위한 선언 과정이 있어야한다.
#include <stdio.h>
struct student // 구조체 선언
{
int num; // int형 멤버
double grade; // double형 멤버
}; // 세미콜론 주의!
int main()
{
struct student s1; // struct student형의 변수 선언 (구조체 변수)
s1.num = 2; // s1의 num 멤버에 2 저장
s1. grade = 2.7; // s1의 grade 멤버에 2.7 저장
printf("학번 : %d\n", s1.num); // num 멤버 출력
printf("학점 : %.1lf\n", s1.grade); // grade 멤버 출력
}
struct : 예약어
student : 구조체 이름
num, grade : 구조체 멤버
구조체 선언 위치에따라 사용할 수 있는 범위가 달라지므로 주의하자
main 함수보다 앞에 선언
☞ 프로그램 전체에서 사용 가능
함수 안에 선언
☞ 함수 안에서만 사용 가능 (선언한 이후부터만)
구조체 변수를 선언해준 후 해당 변수의 멤버를 사용할 수 있다.
struct [구조체명] [구조체_변수명];
struct student s1;
변수를 선언하면 구조체에 대한 저장 공간이 할당된다.
구조체의 멤버에 대한 공간은 메모리에 연속적으로 할당된다.
구조체 변수의 크기는 각 멤버의 크기의 합과 같다. (시스템에 따라 byte alignment 방식을 사용할 경우 크기가 달라질 수 있음)
구조체 변수를 선언한 후 그 안에 있는 멤버에 접근할 때 접근 연산자 .를 사용한다.
구조체_변수명.멤버명
s1.num
이렇게 멤버에 접근하여 변수처럼 사용할 수 있다.
위의 예제에서 s1.num은 int형 변수가 되고 s1.grade는 double형 변수가 된다.
구조체 멤버로는 다양한 자료형을 사용할 수 있다. 기본적인 자료형인 int, double 외에도 배열, 포인터, 다른 구조체도 사용 가능하다.
▼ 배열과 포인터를 멤버로 갖는 구조체 예제
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct profile // 신상명세 구조체
{
char name[20]; // 이름 (배열 멤버)
int age; // 나이
double height; // 키
char *intro; // 자기소개 (포인터 멤버)
};
int main()
{
struct profile yuni; // profile 구조체 변수 선언
strcpy(yuni.name, "서하윤"); // name 배열 멤버에 문자열 복사
yuni.age = 17; // age 멤버에 나이 저장
yuni.height = 164.5; // height 멤버에 키 저장
yuni.intro = "항상 행복하세요."; // 문자열 상수와 포인터 멤버 연결
printf("이름 : %s\n", yuni.name);
printf("나이 : %d\n", yuni.age);
printf("키 : %.1lf\n", yuni.height);
printf("자기소개 : %s\n", yuni.intro);
}
▼ 다른 구조체를 멤버로 갖는 구조체 예제
#include <stdio.h>
struct profile
{
int age; // 나이
double height; // 키
};
struct student
{
struct profile pf; // profile 구조체를 멤버로 사용
int id; // 학번
double grade; // 학점
};
int main()
{
struct student yuni; // student 구조체 변수 선언
yuni.pf.age = 17; // pf 멤버의 age 멤버에 나이 저장
yuni.pf.height = 164.5; // pf 멤버의 height 멤버에 키 저장
yuni.id = 315;
yuni.grade = 4.3;
printf("나이 : %d\n", yuni.pf.age);
printf("키 : %.1lf\n", yuni.pf.height);
printf("학번 : %d\n", yuni.id);
printf("학점 : %.1lf\n", yuni.grade);
}
pf의 멤버인 age와 height에 접근하기 위해서는 yuni의 멤버인 pf에 접근한 후 age에 접근해야 한다.
-> yuni.pf.age
선언과 동시에 초기화하는 것이 가능하다. 이때 여러 개의 멤버를 초기화하므로 배열 초기화처럼 중괄호로 묶어서 초기화한다.
#include <stdio.h>
struct student
{
int id; // 학번
char name[20]; // 이름
double grade; // 학점
};
int main()
{
// 구조체 변수 선언과 초기화
struct student s1 = { 315, "홍길동", 2.4 },
s2 = { 316, "이순신", 3.7 },
s3 = { 317, "세종대왕", 4.4 };
struct student max; // 최고 학점을 저장할 구조체 변수
max = s1; // s1을 최고 학점으로 가정
if (s2.grade > max.grade) max = s2; // s2가 더 높으면 max에 대입
if (s3.grade > max.grade) max = s3; // s3가 더 높으면 max에 대입
printf("학번 : %d\n", max.id);
printf("이름 : %s\n", max.name);
printf("학점 : %.1lf\n", max.grade);
}
▼ 대입 연산 과정
struct student
{
int id;
char name[20];
double grade;
} s1 = { 315, "홍길동", 2.4 }; // 구조체 변수 선언과 초기화
이처럼 구조체 형선언, 변수 선언, 초기화를 동시에 하는 것도 가능하다.
이때 구조체 변수는 전역 변수로 선언된다.
구조체 변수는 대입 연산이 가능하여 함수의 인수로 주거나 함수 안에서 여러 개의 값을 구조체로 묶어서 동시에 반환할 수 있다.
두 변수의 값을 바꾸는 함수는 포인터를 사용해야 하지만 구조체 변수를 사용하면 포인터 없이 함수를 만들 수 있다.
#include <stdio.h>
// 로봇의 시력을 저장할 구조체
struct vision
{
double left; // 왼쪽 눈
double right; // 오른쪽 눈
};
// 두 시력을 바꾸는 함수
struct vision exchange(struct vision robot)
{
double temp; // 교환을 위한 임시 변수
temp = robot.left;
robot.left = robot.right;
robot.right = temp;
return robot; // 구조체 변수 반환
}
int main()
{
struct vision robot;
printf("시력 입력 : ");
scanf("%lf%lf", &(robot.left), &(robot.right)); // 시력 입력
robot = exchange(robot); // 교환 함수 호출
printf("바뀐 시력 : %.1lf %.1lf\n", robot.left, robot.right);
}
로봇의 좌, 우 시력을 입력 받은 후에 함수로 두 값을 바꾸고 다시 출력
함수의 매개변수와 반환형 모두 구조체
☞ 매개변수로 구조체가 넘어가면 그 멤버 값이 복사되어 멤버 값을 바꾸는 인수로 사용되고 바뀐 결과의 구조체를 반환한다.