구조체
보통은 필요할 때마다 변수를 하나씩 선언해서 사용하지만, 여러 개의 변수를 하나로 묶어서 한번에 관리하는 방법이 있다. 만약 인적 정보를 처리한다고 하면 이름, 나이, 주소에 관한 변수를 각각 선언해서 저장하면 되지만, 여러 사람의 인적정보를 각각 구분해서 저장하고 싶다면 코드가 굉장히 복잡해지고 비효율적이다. 이럴 때 자료를 조금 더 체계적으로 관리하기 위해 사용하는 것이 구조체이다.
기본적인 구조체 정의 방법은 다음과 같다.
struct Person{
char name[20];
int age;
char address[100];
};
Person이라는 구조체 안에 이름, 나이, 주소를 저장할 수 있도록 선언하는 법이다. 이렇게 정의한 구조체를 사용하는 방법은 다음과 같다.
struct Person people[100];
위의 코드는 Person형 변수를 배열로서 100개 선언한 것이다. 이렇게 한 줄로, 100명의 이름, 나이, 주소를 각각 저장할 수 있는 변수를 선언한 것이다. 구조체 변수의 사용방법은 다음과 같다.
#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); // 나이: 30
printf("주소: %s\n", p1.address); // 주소: 서울시 용산구 한남동
return 0;
}
구조체를 정의함과 동시에 p1이라는 이름으로 변수를 선언해주었다. 또, 구조체 변수 p1의 이름, 나이, 주소에 각각 접근하는 법은 변수이름 뒤에 점을 붙이고 접근하고자 하는 멤버를 쓰면된다. 예를 들어 p1이라는 구조체 변수의 이름에 접근하고 싶다면 p1.name 이 되고 나이에 접근하고 싶다면 p1.age가 된다. 구조체를 이용해서 이미 많이 편리해졌지만, 구조체 변수를 선언할 때마다 struct를 써주는 게 귀찮다면, 다음과 같이 구조체를 정의하면 번거로움을 줄일 수 있다.
typedef struct 구조체이름{
자료형 멤버이름;
} 별칭;
위와 같이 선언한다면, 변수를 선언할 때 자료형 변수이름으로 선언하는 것과 같이 별칭 변수이름 만으로 구조체 변수를 선언할 수 있다.
하지만, 어차피 구조체를 선언할 때마다 별칭을 달아서 별칭으로 구조체를 선언하고 사용할 것이라면, 구조체의 이름을 왜 정해주어야 하나 생각이 들 수도 있다. 그래서 C언어에선 구조체의 이름을 생략하고 선언하는 것을 허락해준다. 이를 익명 구조체라 한다.
typedef struct {
자료형 멤버이름;
} 구조체별칭;
이름을 생략한 경우에는 별칭을 무조건 지정해주어야 한다.
구조체 포인터
구조체는 멤버 변수를 여러 개 포함하고 있기 때문에 비교적 크기가 클 수 밖에 없다. 그래서 구조체는 포인터에 메모리를 할당해서 사용하는 편이 효율적이다. 구조체 포인터를 사용할 때에도 malloc() 함수를 사용한다.
#define _CRT_SECURE_NO_WARNINGS // strcpy 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h> // strcpy 함수가 선언된 헤더 파일
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일
struct Person { // 구조체 정의
char name[20]; // 구조체 멤버 1
int age; // 구조체 멤버 2
char address[100]; // 구조체 멤버 3
};
int main()
{
struct Person *p1 = malloc(sizeof(struct 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); // 서울시 용산구 한남동
free(p1); // 동적 메모리 해제
return 0;
}
먼저 구조체를 정의한 후에, 구조체 변수를 선언할 때 포인터 변수로 선언하며, malloc() 함수를 이용해 메모리를 할당한다. 이 때, 할당하는 메모리의 크기는 sizeof(struct Person)으로 해주면 된다. 구조체의 크기는 이름을 저장하는 문자형 배열 20개(20Byte), 나이를 저장하는 정수형 변수 1개(4Byte), 주소를 저장하는 문자형 배열 120개(120Byte)로 총 144Byte이다. sizeof(struct)는 144를 반환하며, 144Byte 크기를 구조체 포인터 변수 p1에 할당한다.
구조체의 멤버에 접근하는 법은 변수이름.멤버이름 과 같이 .(점)을 이용해서 접근하였지만, 구조체를 포인터로 선언하게 되면 . 대신 ->(화살표 연산자)를 이용해서 접근해야 한다. 만약 점을 이용해서 접근하고자 한다면 (*p1).name과 같이 역참조 연산자를 이용해야 한다. 만약, 구조체의 멤버가 포인터로 선언되어서, 이에 접근하고자 한다면 *p1->name 혹은 *p1.name과 같이 괄호를 쓰지 않고 역참조 해야 한다. 이 때의 역참조는 구조체에 대한 역참조가 아닌 구조체의 멤버에 대한 역참조이다. 만약 구조체 포인터 d2의 포인터 변수 name에 접근하고자 한다면 *(*d2).name과 같이 사용해야 한다. 마지막으로, 구조체 포인터도 동적으로 할당했기 때문에 free() 함수를 이용해 할당을 해제해주어야 한다.
#48.4 퀴즈
구조체를 선언하는 키워드는 struct이다. 답은 c.
struct 키워드로 선언하며, 닫는 괄호로 마무리 짓고 끝에 세미콜론을 붙여야한다. 답은 c.
별칭을 사용할 땐 typedef 키워드를 사용한다. 답은 d.
이름 없이 정의하는 구조체를 익명 구조체라고 한다.
#48.5 연습문제: 좌표 구조체 정의하기
#include <stdio.h>
①
struct______________
____________________
____________________
____________________
int main()
{
②________________ p1;
③_______________
p1.y = 20;
printf("%d %d\n", p1.x, p1.y);
return 0;
}
실행 결과
10 20
x좌표, y좌표 두 개의 정수형 변수를 저장해야 하기 때문에 구조체 멤버는 int형 변수 2개로 선언해야 한다. 출력을 보면 p1.x p1.y로 접근하기 때문에 멤버의 이름은 각각 x, y가 되어야 한다.
1.
Point2D {
int x;
int y;
};
2. struct Point2D
3. p1.x = 10;
#48.6 연습문제: typedef로 좌표 구조체 정의하기
#include <stdio.h>
①
typedef______________
_____________________
_____________________
_____________________
int main()
{
Point2D ②_____;
p1.x = 10;
③_______________
printf("%d %d\n", p1.x, p1.y);
return 0;
}
실행 결과
10 20
별칭은 Point2D로 정해주기 때문에 구조체의 이름은 __Point2D와 같이 의미만 알 수 있게끔 선언해주면 된다.
1.
struct __Point2D{
int x;
int y;
} Point2D;
2. p1
3.p1.y = 20;
#48.7 연습문제: 익명 구조체로 좌표 구조체 정의하기
#include <stdio.h>
typedef struct {
int x;
int y;
________________
int main()
{
Point2D p1;
p1.x = 10;
p1.y = 20;
printf("%d %d\n", p1.x, p1.y);
return 0;
}
익명 구조체는 별칭을 꼭 써주어야 한다. main함수에 선언된 구조체를 보면 Point2D로 선언되었기 때문에 별칭을 Point2D로 지어주면 된다.
} Point2D;
#48.8 심사문제: 자동차 계기판 구조체 선언하기
자동차에서 속도, 연료, 주행거리, 엔진 온도, 회전수를 표현하는 계기판 구조체가 정의되어 있습니다. 다음 소스 코드를 완성하여 계기판 정보가 출력되게 만드세요.
#include <stdio.h>
struct Dashboard {
int speed;
char fuel;
float mileage;
int engineTemp;
int rpm;
};
int main()
{
__________________
__________________
__________________
__________________
__________________
__________________
printf("Speed: %dkm/h\n", d1.speed);
printf("Fuel: %c\n", d1.fuel);
printf("Mileage: %fkm\n", d1.mileage);
printf("Engine temp: %d℃\n", d1.engineTemp);
printf("RPM: %d\n", d1.rpm);
return 0;
}
실행 결과
Speed: 80km/h
Fuel: F
Mileage: 5821.442871km
Engine temp: 200℃
RPM: 1830
Dashboard 구조체형 변수를 선언한 후에, 각 멤버에 값을 저장해주면 된다.
struct Dashboard d1;
d1.speed = 80;
d1.fuel = 'F';
d1.mileage = 5821.442871;
d1.engineTemp = 200;
d1.rpm = 1830;
#48.9 심사문제: 자동차 계기판 구조체 정의하기
#include <stdio.h>
_____________________
_____________________
_____________________
_____________________
_____________________
_____________________
_____________________
int main()
{
Dashboard d1;
d1.speed = 80;
d1.fuel = 'F';
d1.mileage = 5821.442871f;
d1.engineTemp = 200;
d1.rpm = 1830;
printf("Speed: %dkm/h\n", d1.speed);
printf("Fuel: %c\n", d1.fuel);
printf("Mileage: %fkm\n", d1.mileage);
printf("Engine temp: %d℃\n", d1.engineTemp);
printf("RPM: %d\n", d1.rpm);
return 0;
}
main 함수를 보면 구조체의 이름은 Dashboard이고 멤버는 speed, fuel, mileage, engineTemp, rpm 5개가 있다. 이를 기반으로 구조체를 정의하면 된다. 이 때, 구조체 선언이 struct Dashboard d1이 아닌 Dashboard d1이므로, 이는 구조체의 이름이 아닌 별칭임을 알 수 있다.
typedef struct _Dashboard{
int speed;
char fuel;
float mileage;
int engineTemp;
int rpm;
} Dashboard;
#49.4 퀴즈
구조체 포인터에 메모리를 할당할 때 크기를 sizeof(struct 구조체이름)과 같은 형태로 사용해야 한다. 답은 4.
구조체 포인터의 멤버에 접근할 때에는 화살표 연산자(->)를 사용한다. 답은 d.
구조체 포인터에 구조체 변수를 할당하는 법은 포인터에 변수를 할당할 때와 동일하다. 답은 d.
#49.5 연습문제: 학생 구조체 포인터에 메모리 할당하기
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Student {
char name[20];
int grade;
int class;
float average;
};
int main()
{
struct Student *s1 = ①___________________________;
②_______________________________
________________________________
________________________________
________________________________
printf("이름: %s\n", s1->name);
printf("학년: %d\n", s1->grade);
printf("반: %d\n", s1->class);
printf("평균점수: %f\n", s1->average);
free(s1);
return 0;
}
실행 결과
이름: 고길동
학년: 1
반: 3
평균점수: 65.389999
먼저 구조체 포인터 s1에 Student 구조체의 크기만큼 할당해야 한다. 그 후에 각 멤버에 화살표 연산자를 이용해 접근해서 값을 저장하면 된다.
1.malloc(sizeof(struce Student));
2.
strcpy(s1->name, "고길동");
s1->grade = 1;
s1->class = 3;
s1->average = 65.389999;
#49.6 연습문제: 3차원 좌표 구조체 포인터에 메모리 할당하기
#include <stdio.h>
#include <stdlib.h>
typedef struct _Point3D {
float x;
float y;
float z;
} Point3D;
int main()
{
Point3D *p1 = ①__________________________
②_____________
_____________
_____________
printf("%f %f %f\n", p1->x, p1->y, p1->z);
free(p1);
return 0;
}
실행 결과
10.000000 20.000000 30.000000
포인터 구조체 p1을 동적할당 받고 각각의 멤버에 접근해서 값을 저장하면 된다. 이 때에는 구조체의 별칭이 있기 때문에 malloc(sizeof(struct _Point3D))가 아닌 malloc(sizeof(Point3D))로 사용한다.
1. malloc(sizeof(Point3D));
2.
p1->x = 10.0;
p1->y = 20.0;
p1->z = 30.0;
#49.7 연습문제: 구조체 포인터에 구조체 주소 할당하기
#include <stdio.h>
#include <stdbool.h>
struct Item {
char name[100];
int price;
bool limited;
};
int main()
{
struct Item item1 = { "베를린 필하모닉 베토벤 교향곡 전집", 100000, false };
①____________________
②____________________
ptr->limited = true;
if (ptr->limited == true)
printf("한정판\n");
else
printf("일반판\n");
return 0;
}
구조체 포인터를 이용해 item1을 할당 받아야 한다.
1. suruct Item * ptr;
2. ptr = &item1;
#49.8 심사문제: 사람과 자동차 구조체 포인터에 메모리 할당하기
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Person {
char name[20];
int age;
char address[100];
};
typedef struct {
char name[20];
int number;
int displacement;
} Car;
int main()
{
_________________________
_________________________
_________________________
_________________________
_________________________
_________________________
_________________________
_________________________
printf("이름: %s\n", p1->name);
printf("나이: %d\n", p1->age);
printf("주소: %s\n", p1->address);
printf("자동차 이름: %s\n", c1->name);
printf("자동차 번호: %d\n", c1->number);
printf("배기량: %dcc\n", c1->displacement);
free(p1);
free(c1);
return 0;
}
실행 결과
이름: 고길동
나이: 40
주소: 서울시 서초구 반포동
자동차 이름: 스텔라
자동차 번호: 3421
배기량: 2000cc
Person 포인터 구조체와 Car 포인터 구조체를 각각 선언한 후에, 멤버에 접근해서 출력 값과 동일하게 값을 저장해주어야 한다. p1과 c1 모두 화살표 연산자(->)로 멤버에 접근하므로, 둘 다 포인터로 선언해주어야 한다.
struct Person *p1 = malloc(sizeof(struct Person));
Car *c1 = malloc(sizeof(Car));
strcpy(p1->name, "고길동");
p1->age = 40;
sprintf(p1->address, "서울시 서초구 반포동");
strcpy(c1->name, "스텔라");
c1->number = 3421;
c1->displacement = 2000;
#49.9 심사문제: 구조체 포인터에 구조체 변수의 주소 할당하기
#include <stdio.h>
struct Point3D {
float x;
float y;
float z;
};
int main()
{
struct Point3D p1 = { 10.0f, 20.0f, 30.0f };
struct Point3D *ptr;
______________
printf("%f %f %f\n", ptr->x, ptr->y, ptr->z);
return 0;
}
실행 결과
10.000000 20.000000 30.000000
Point3D 구조체 변수 p1의 멤버에 저장된 값을 포인터 구조체 ptr을 통해 출력하였기 때문에 포인터 구조체 ptr에 p1을 할당해주면 된다.
ptr = &p1;
#50.2 연습문제: 사각형의 넓이 구하기
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
struct Rectangle {
int x1, y1;
int x2, y2;
};
int main()
{
struct Rectangle rect;
int area;
rect.x1 = 20;
rect.y1 = 20;
rect.x2 = 40;
rect.y2 = 30;
①________________________________
②________________________________
③________________________________
printf("%d\n", area);
return 0;
}
실행 결과
200
두 점 (20, 20)과 (40, 30)을 마주보는 꼭지점으로 하는 직사각형의 넓이를 출력해야 한다. 먼저 높이는 x좌표의 차, 넓이는 y좌표의 차이기 때문에 각각을 width, height 변수를 선언해서 저장해준 다음, 그 둘을 곱한 값을 area 변수에 저장한다. 넓이와 높이를 구할 땐, 어느 변수가 더 큰 값인지 모르기 때문에 두 변수를 뺀 후 abs() 함수를 통해 절대값을 반환해야 한다.
1. int width = abs(rect.x2 - rect.x1);
2. int height = abs(rect.y2 - rect.y1);
3. area = width * height;
#50.3 심사문제: 두 점 사이의 거리 구하기
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>
struct Point2D {
int x;
int y;
};
int main()
{
struct Point2D p1;
struct Point2D p2;
double distance;
scanf("%d %d %d %d", &p1.x, &p1.y, &p2.x, &p2.y);
____________________________
____________________________
____________________________
printf("%f\n", distance);
return 0;
}
빈 칸에 알맞은 코드를 작성해서 distance 변수에 두 점 사이의 거리를 넣어야 한다. 먼저 math.h 헤더파일에 선언된 sqrt() 함수를 이용해서 p1.x와 p2.x의 차의 제곱과 p1.y와 p2.y의 차의 제곱을 더한 값의 제곱근을 출력한다.
int distanceX = abs(p1.x - p2.x);
int distanceY = abs(p1.y - p2.y);
distance = sqrt(distanceX * distanceX + distanceY * distanceY);
'Programming > C' 카테고리의 다른 글
[ProjectH4C] 코딩도장 Unit 54~55 Write-Up (0) | 2020.07.25 |
---|---|
[ProjectH4C] 코딩도장 Unit 51~53 Write-Up (0) | 2020.07.25 |
[ProjectH4C] 코딩도장 Unit 46~47 Write-Up (0) | 2020.07.24 |
[ProjectH4C] 코딩도장 Unit 41~45 Write-Up (0) | 2020.07.24 |
[ProjectH4C] 코딩도장 Unit 39~40 Write-Up (0) | 2020.07.23 |