사자자리
[C언어] 6주차 구조체 본문
#include <stdio.h>
#include <string.h>
struct student{ //구조체 정의
char name[20]; //구조체의 멤버들
int kor, eng, math;
}s1; //변수명 정의
int main(){
strcpy(s1.name, "Sirius"); //점(.)으로 구조체의 멤버에 접근하여 값 할당
s1.kor = 90;
s1.eng = 95;
s1.math = 100;
struct student s2 = {"Regulus", 85, 90, 100}; //새 구조체 변수 선언, 초기화
printf("%s의 성적: 국어 %d점, 영어 %d점, 수학 %d점\n", s1.name, s1.kor, s1.eng, s1.math);
printf("%s의 성적: 국어 %d점, 영어 %d점, 수학 %d점\n", s2.name, s2.kor, s2.eng, s2.math);
return 0;
}
<실행 결과>
Sirius의 성적: 국어 90점, 영어 95점, 수학 100점
Regulus의 성적: 국어 85점, 영어 90점, 수학 100점
구조체
- struct: data structure의 약어
- 관련된 변수들을 하나로 묶어서 사용할 수 있다.
구조체의 정의
- 보통 함수 바깥에 정의한다. 함수 안에 정의하면, 해당 함수 안에서만 구조체를 사용할 수 있다.
- 멤버: 구조체 안에 들어있는 변수
- 구조체를 정의한다고 해서, 구조체의 멤버가 메모리에 할당되지 않는다.
- 구조체의 변수를 선언하면, 구조체의 멤버가 메모리에 할당된다.
struct 구조체명{
데이터형 멤버명;
데이터형 멤버명;
...
}변수명, 변수명... ; //변수명은 생략 가능, 세미콜론은 필수
//예시
struct student{ //구조체 정의
char name[5]; //구조체의 멤버들
int kor, eng, math;
}s1; //변수명 정의
구조체의 크기
- 모든 멤버들의 크기의 합보다 크거나 같다.
- 메모리 정렬 때문에 멤버들 사이에 패딩이 들어갈 수도 있기 때문이다.
- sizeof ( )
구조체 변수의 선언
struct 구조체명 변수명;
struct student s2;
- 구조체 변수를 선언하면, 구조체 변수가 가진 멤버들이 메모리에 선언된 순서대로 할당된다.
#include <stdio.h>
struct student{ //구조체 정의
char name[5];
int kor, eng, math;
};
int main(){
struct student s1; //구조체 선언
}
- 구조체를 정의하면서 변수도 함께 선언할 때, 구조체명을 생략할 수 있다.
- 그러나 구조체명이 없으므로 나중에 새로운 변수를 선언하지 못한다.
#include <stdio.h>
#include <string.h>
struct{ //구조체명 생략
char name[10];
int age;
}s1;
int main(){
strcpy(s1.name, "Harry");
s1.age = 11;
struct s2;
s2.age = 11; //컴파일 에러
struct s3 = {"Hermione", 11}; //컴파일 에러
}
구조체 변수의 초기화
- { } 안에 멤버들의 초기값을 순서대로 나열한다.
- 초기값의 개수가 멤버의 개수보다 부족하면, 나머지는 0으로 초기화된다.
구조체 간의 초기화 및 대입
- 같은 구조체의 변수들끼리는 서로 초기화나 대입이 가능하다.
- 얕은 복사
#include <stdio.h>
struct profile{
int birthyear;
int birthday;
};
int main(){
struct profile p1 = {1959, 1103};
struct profile p2 = {1961, 0823};
struct profile p3 = p1; //구조체 간의 초기화
struct profile p4;
p4 = p2; //구조체 간의 대입
printf("p1의 주소: %p = p3의 주소: %p", p1, p3);
printf("p2의 주소: %p = p4의 주소: %p", p2, p4);
}
<실행 결과>
p1의 주소: 0000044F000007A7 = p3의 주소: 0000044F000007A7
p2의 주소: 000004CF00000786 = p4의 주소: 000004CF00000786
구조체 간의 비교
- 변수끼리는 직접 비교 연산을 할 수 없다. 변수형이 다를 수 있기 때문이다.
- 멤버끼리는 직접 비교 연산을 할 수 있다.
//변수 간의 비교: 컴파일 에러
p1 == p2
//멤버 간의 비교: 가능
p1.birthday == p2.birthday
구조체 배열
- 같은 구조체의 변수를 여러 개 사용하려면 구조체 배열을 선언한다.
struct student arr[3]; //크기가 3인 student 구조체 배열을 선언
- 구조체 배열도 인덱스를 이용해서 배열의 원소에 접근한다.
#include <stdio.h>
struct student{
char name[4];
int id;
};
int main(){
struct student arr[3];
printf("학생의 정보를 입력하세요.\n");
for (int i = 0; i < 3; i++){
printf("이름: ");
scanf("%s", arr[i].name);
printf("학번: ");
scanf("%d", arr[i].id);
}
for (int i = 0; i < 3; i++){
printf("이름: %s, 학번: %d\n", arr[i].name, arr[i].id);
}
return 0;
}
<실행 결과>
학생의 정보를 입력하세요.
이름: Mon
학번: 2211111
이름: Tue
학번: 2222222
이름: Wed
학번: 2233333
이름: Mon, 학번: 2211111
이름: Tue, 학번: 2222222
이름: Wed, 학번: 2233333
구조체 포인터
- 구조체 변수의 주소를 저장하는 포인터
- 구조체 포인터로 구조체의 멤버에 접근할 때, -> 연산자를 사용한다.
#include <stdio.h>
struct student{
char name[20];
int id;
};
int main(){
struct student sirius = {"Sirius Black", 691103};
struct student *p = &sirius; //구조체 포인터 선언, 초기화
printf("이름: %s, 학번: %d", p->name, p->id); // -> 연산자 사용
return 0;
}
<실행 결과>
이름: Sirius Black, 학번: 691103
malloc 함수
- Memory ALLOCation
- 사용할 메모리 공간을 확보하는 함수
- stdlib.h 헤더 파일에 선언되어 있다.
- 동적 메모리 할당: 원하는 시점에 원하는 크기만큼 메모리를 할당할 수 있다.
사용하는 메모리 | 메모리 해제 | |
일반 변수 | 스택(stack) | 사용한 뒤 따로 처리하지 않아도 된다. |
malloc 함수 | 힙(heap) | free 함수로 메모리를 해제해야 한다. |
메모리 누수(memory leak): 메모리를 할당만 하고 해제하지 않아 메모리 사용량이 계속 증가하는 현상
#include <stdio.h>
#include <stdlib.h>
int main(){
int n = 10;
int *p1 = &n;
int *p2;
p2 = malloc(sizeof(int)); //4바이트만큼 동적 메모리 할당
printf("p1이 가리키는 메모리 주소: %p\n", p1);
printf("p2가 가리키는 메모리 주소: %p\n", p2);
free(p2); //동적으로 할당한 메모리 해제
return 0;
}
<실행 결과>
p1이 가리키는 메모리 주소: 000000000062FE0C
p2가 가리키는 메모리 주소: 00000000001713E0
비트 필드
- 지금까지 구조체의 멤버는 각 데이터형의 크기만큼 공간을 차지했다. ex) int: 4 byte
- 비트 필드를 사용하면 구조체의 멤버를 비트 단위로 저장할 수 있다. (8 bits = 1byte)
- 주어진 비트로 표현 가능한 범위 밖의 값을 저장하면 오버플로우가 일어난다.
- 비트 필드의 각 멤버는 최하위 비트부터 차례대로 배치된다.
- 중간에 일부 비트를 비워둘 수 있다.
struct 구조체명{
정수자료형 멤버명 : 비트수;
};
#include <stdio.h>
struct time{
unsigned int hour : 5;
unsigned int : 3; // 3 비트는 사용하지 않는다.
unsigned int min : 6;
unsigned int sec : 6;
};
int main(){
struct time t1;
t1.hour = 23; // 23: 10111, 비트 5개
t1.min = 59; // 59: 111011, 비트 6개
t1.sec = 59; // 59: 111011, 비트 6개
printf("현재 시간: %d시 %d분 %d초", t1.hour, t1.min, t1.sec);
return 0;
}
<실행 결과>
현재 시간: 23시 59분 59초
공용체
- 여러 멤버들이 메모리를 공유해서 사용한다.
- 공용체 변수의 멤버들을 모두 같은 주소에 할당된다.
- 공용체의 크기는 멤버 중 가장 크기가 큰 멤버에 의해 결정된다.
- 공용체 변수를 초기화할 때는 첫 번째 멤버의 초기값만 지정한다.
#include <stdio.h>
union data{
unsigned long word;
unsigned char byte[4];
};
int main(){
union data d1;
printf("공용체 data의 크기: %d\n", sizeof(union data));
d1.word = 0x12345678;
printf("d1.word = %x\n", d1.word);
for (int i = 0; i < 4; i++){
printf("d1.byte[%d] = %x\n",i, d1.byte[i]);
}
return 0;
}
<실행 결과>
공용체 data의 크기: 4
d1.word = 12345678
d1.byte[0] = 78
d1.byte[1] = 56
d1.byte[2] = 34
d1.byte[3] = 12
리틀 엔디언 | 최하위 바이트부터 메모리에 저장하는 방식 | 인텔 계열의 CPU |
빅 엔디언 | 최상위 바이트부터 메모리에 저장하는 방식 | 모토로라 계열의 CPU |
열거체
- enum: enumeration(열거, 목록)
- 가독성 향상: 정수형 상수에 이름을 붙여서 코드를 이해하기 쉽게 해준다.
#include <stdio.h>
enum week {sun, mon, tue, wed, thur, fri, sat}; //sun = 0, mon = 1, ... sat = 6으로 정의
enum color {red = 10, green = 20, blue = 30}; //red = 10, green = 20, blue = 30으로 정의
int main(){
enum week weekend = sat; //열거형 변수 선언, 값 할당
enum color ravenclaw = blue;
printf("%d\n", weekend);
printf("%d\n", ravenclaw);
return 0;
}
<실행 결과>
6
30
typedef
- 기존의 데이터형에 새로운 이름을 붙인다.
- typedef 정의를 한 다음에도 원래의 데이터형을 그대로 사용할 수 있다.
typedef 기존 데이터형 새 이름;
typedef unsigned int UNIT; //기본형
typedef char* STR; //파생형
typedef struct student POINT; //사용자 정의형
- typedef와 구조체
#include <stdio.h>
struct student{ //구조체 정의
int kor, eng, math;
};
typedef struct student POINT; //typedef 정의
int main(){
POINT s1 = {100, 90, 80};
printf("s1의 점수: 국어 %d 영어 %d 수학 %d", s1.kor, s1.eng, s1.math);
return 0;
}
<실행 결과>
s1의 점수: 국어 100 영어 90 수학 80
#include <stdio.h>
typedef struct student{ //구조체 정의, typedef 정의
int kor, eng, math;
}POINT;
int main(){
POINT s1 = {100, 90, 80};
printf("s1의 점수: 국어 %d 영어 %d 수학 %d", s1.kor, s1.eng, s1.math);
return 0;
}
<실행 결과>
s1의 점수: 국어 100 영어 90 수학 80
[코딩도장] 48.6 연습문제: typedef로 좌표 구조체 정의하기
다음 소스 코드를 완성하여 2차원 좌표 x, y를 표현하는 구조체를 정의하고, 10 20이 출력되게 만드세요. 좌표의 자료형은 int입니다.
#include <stdio.h>
typedef ①__________
int main()
{
Point2D ②__________
p1.x = 10;
③__________
printf("%d %d\n", p1.x, p1.y);
return 0;
}
답
#include <stdio.h>
typedef struct grid{
int x, y;
}Point2D;
int main()
{
Point2D p1;
p1.x = 10;
p1.y = 20;
printf("%d %d\n", p1.x, p1.y);
return 0;
}
[코딩도장] 49.6 연습문제: 3차원 좌표 구조체 포인터에 메모리 할당하기
3차원 좌표 구조체 _Point3D가 정의되어 있습니다. 다음 소스 코드를 완성하여 10.000000 20.000000 30.000000이 출력되게 만드세요.
#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;
}
답
#include <stdio.h>
#include <stdlib.h>
typedef struct _Point3D {
float x;
float y;
float z;
} Point3D;
int main()
{
Point3D *p1 = malloc(sizeof(Point3D));
p1 -> x = 10;
p1 -> y = 20;
p1 -> z = 30;
printf("%f %f %f\n", p1->x, p1->y, p1->z);
free(p1);
return 0;
}
'C언어 > C언어 이론' 카테고리의 다른 글
[C언어] 7주차 포인터의 활용 (0) | 2022.06.26 |
---|---|
[C언어] 6주차 함수의 활용 (0) | 2022.06.25 |
[C언어] 5주차 포인터 (0) | 2022.05.20 |
[C언어] 5주차 배열과 문자열 (0) | 2022.05.20 |
[C언어] 4주차 함수 (0) | 2022.05.14 |