You are on page 1of 43

구조체 (Structure)

이주현 교수
( joohyunlee@hanyang.ac.kr)

Division of Electrical Engineering


Hanyang University, ERICA Campus
2

목차

■ 서로 다른 타입의 데이터를 하나로 묶어서 표현할 수 있는 방법의 필요성

■ 구조체의 정의

■ 구조체 변수로 가능한 것과 불가능한 것

■ 구조체의 정의에 포함되는 typedef 선언과 구조체 배열

■ 구조체의 추가적인 특성과 메모리 관계


3

구조체의 필요성
■ 학생에 대한 데이터를 하나로 모으려면?

int number;
char name[10];
double grade;
와 같이 개별 변수
로 나타낼 수 있지
만 묶을 수가 있나?
4

구조체의 필요성

구조체를 사용
하면 변수들을
하나로 묶을 수
있습니다.
5

구조체 정의

구조체
그룹을 지어줘야 POINT 정의
하는 데이터 변수

double xPos;
double yPos;

구조체를 기반으로
변수 xPos와 yPos
를 묶음
6

구조체 변수 선언
■ 구조체 정의와 구조체 변수 선언은 다르다.
7

구조체의 초기화
■ 중괄호를 이용하여 초기값을 나열한다.

struct student {
int number;
char name[10];
double grade;
};
struct student s1 = { 24, "Kim", 4.3 };
8

구조체 정의가 의미하는 것

struct point
{ ∙ 정의가 이뤄지고 난 다음부터 point는 변수의 선언
double xPos; 에 사용되는 자료형의 이름으로 인식 됨.
double yPos; ∙ 즉, point라는 이름은 프로그래머가 정의한 자료형
};

기본 자료형 double과
구조체 자료형 point
double point 비교

제공 방식 기본적으로 제공 프로그래머의 정의에 의해 제공

double num; struct point pnt;


변수 선언
double num1, num2; struct point p1, p2;
pnt.xPos=10;
접근 방식 num=20;
pnt.yPos=20;
9

선의 길이를 계산하여 출력하는 프로그램


#include <math.h>
double CalDist(double x1, double y1,
#define POINT_NUMBER 5
double x2, double y2)
{
double CalDist(double x1, double y1,
double xDist=x1-x2;
double x2, double y2);
double yDist=y1-y2;
double CalLineLen(double xpos[], double ypos[]);
return sqrt(xDist*xDist+yDist*yDist);
int main(void) 점의 위치 정보를 }
{ 저장하기 위해 선
double lineLen=0;
double xPosArr[POINT_NUMBER]; 언된 배열 – 두 점 사이의 거리를 반환하는 함수
double yPosArr[POINT_NUMBER]; – sqrt 함수는 헤더파일 math.h에 선언된
함수로서 제곱근의 계산 결과를 반환
int i;
for(i=0; i<POINT_NUMBER; i++)
{
printf("점 %d의 좌표 입력: ", i+1); double CalLineLen(double xpos[], double ypos[])
scanf("%lf %lf", &xPosArr[i], &yPosArr[i]); {
} double len=0;
상수 POINT_NUMBER의
int i;
개수만큼 점의 위치 정보 for(i=0; i<POINT_NUMBER-1; i++)
를 입력 받는다. len+=CalDist(xpos[i], ypos[i],
xpos[i+1], ypos[i+1]);
lineLen=CalLineLen(xPosArr, yPosArr);
printf("라인의 길이: %g \n", lineLen); return len;
return 0; }
}
10

소프트웨어에서 표현하는 데이터들의 일반적인 특징


■ 예제에서 표현하고 있는 점의 좌표 정보가 지니는 특징

x좌표가 입력될 때, y좌표도 입력


x좌표의 정보와 y좌표의 정보
는 함께 이동한다.
CalDist 함수의 인자로 x 좌표가
전달될 때 y좌표도 전달

■ 데이터들의 일반적인 특징
▶ 소프트웨어 개발 과정에서 표현하는 데이터들은 그룹을 형성한다.

주소
록관
프로 리
그램

이름, 주소, 전화번호 그룹 형성 구조체의 필요성

그룹을 형성하는 데이터들


도서 을 묶어서 관리하면 데이터
관리
프로
그램 들을 관리하기가 수월하다
그룹 형성
제목, 정가, ISBN, 출판사
11

구조체 활용과 멤버의 접근


일반 변수와 구조체
struct point
변수의 메모리 구성
{
double xPos;
double yPos;
};

int main(void)
{
double num;
struct point pnt;

num=1.2; 구조체 변수 선언의 경우


pnt.xPos=2.2; 키워드 struct를 붙여야
pnt.yPos=3.4;
한다.
printf("num: %g \n", num);
printf("pnt.xPos: %g \n", pnt.xPos);
printf("pnt.yPos: %g \n\n", pnt.yPos); ∙ 변수 pnt는 두 개의 변수 xPos와 yPos로
이뤄져 있기 때문에, xPos로 접근할 지
printf("num의 크기: %d바이트\n", sizeof(num));
printf("pnt의 크기: %d바이트\n", sizeof(pnt)); yPos로 접근할지를 명시해야 함
∙ 구조체 변수의 멤버에 접근하기 위해서
return 0;
는 dot(.) 연산자 사용
}
12

Typedef 이용 구조체 선언

typedef
struct point struct point 사용
{ {
double xPos; double xPos;
double yPos; double yPos; POINT가 struct point를
}; };
대신하도록 typedef 선언

int main(void) typedef struct point POINT;


{
struct point pnt; int main(void)
.... {
return 0; POINT pnt;
} .... 컴파일러는 위의 typedef선언
return 0; 을 반영하여 POINT를 struct
} point로 인식.
13

typedef를 활용한 구조체 변수 선언 예


struct point point와 POINT가 모두
{ struct point를 대신하
double xPos;
double yPos; 도록 선언
};

typedef struct point point;


typedef struct point POINT;

int main(void) typedef선언 덕분에


{
point p1;
구조체 변수의 선언이
POINT p2; 간단해졌음

p1.xPos=0.1, p1.yPos=0.2;
p2.xPos=2.4, p2.yPos=2.5;

printf("X축 거리: %g \n", p2.xPos-p1.xPos);


printf("Y축 거리: %g \n", p2.yPos-p1.yPos);
return 0;
}
14

typedef과 #define 비교
■ 이식성을 높여준다.
▶ 코드를 컴퓨터 하드웨어에 독립적으로 만들 수 있다
▶ (예) int형은 2바이트이기도 하고 4바이트, int형 대신에 typedef을 이용
한 INT32나 INT16을 사용하게 되면 확실하게 2바이트인지 4바이트인지
를 지정할 수 있다.

■ #define을 이용해도 typedef과 비슷한 효과를 낼 수 있다. 즉 다음과


같이 INT32를 정의할 수 있다.
▶ #define UINT32 unsigned int
▶ typedef float VECTOR[2];// #define으로는 불가능하다.

■ 문서화의 역할도 한다.


▶ typedef을 사용하게 되면 주석을 붙이는 것과 같은 효과
15

구조체 변수의 활용

■ 함수의 인자로 전달 및 반환 가능

■ 대입연산자의 피연산자로 사용 가능

■ 구조체 변수를 이용한 사칙연산은 불가능

▶ 구조체 멤버에는 문자열의 저장이 가능한 char형 배열이 올 수도 있으므로

사칙연산에 의미를 부여하기 어렵다.


16

구조체 변수의 활용방법(함수의 인자로 전달과 반환)


struct __point
{
double xPos;
double yPos;
}; IncrePos 함수는 point형 데이터
typedef struct __point point; 를 인자로 받고, point형 데이터
를 반환하는 함수
point IncrePos(point pnt)
{
pnt.xPos++;
pnt.yPos++;
return pnt;
}

int main(void)
{
point p1, p2, p3;
p1.xPos=0.5;
p1.yPos=1.5;

p2=p1;
p3=IncrePos(p2);
....
}
17

구조체 변수의 활용방법(구조체 변수를 이용한 대입연산)


struct __point
{
double xPos;
double yPos;
};
typedef struct __point point;

point IncrePos(point pnt)


{
pnt.xPos++; p1 p2
pnt.yPos++;
return pnt; xPos : 0.5 xPos : 0.5
}
yPos : 1.5 yPos : 1.5
int main(void)
{
point p1, p2, p3;
p1.xPos=0.5;
p1.yPos=1.5;
p1의 모든 값은 p2에 복사 됨
p2=p1;
p3=IncrePos(p2);
....
}
18

구조체 변수를 이용한 사칙연산은 불가능

struct __person ∙ 구조체 멤버에는 char형 배열처럼 사칙연산


{
을 적용하기 어려운 대상도 포함될 수 있다.
char name[NAME_LEN];
unsigned int age; ∙ 따라서 구조체 변수를 이용한 사칙연산은
}; 허용되지 않는다.
typedef struct __person PERSON;

int main(void)
{
PERSON p1, p2; 구조체 변수를
....
이용한 사칙연산
p1=p1+p2; 불가능
....
}
19

구조체 변수 활용 방법
struct __point
{
double xPos;
double yPos;
};
typedef struct __point point;

point IncrePos(point pnt)


{
pnt.xPos++;
pnt.yPos++;
return pnt;
}

int main(void)
{
point p1, p2, p3;
p1.xPos=0.5;
p1.yPos=1.5;

p2=p1;
p3=IncrePos(p2);

printf("X: %g \n", p3.xPos);


printf("Y: %g \n", p3.yPos);
return 0;
}
20

배열을 멤버로 하는 구조체의 정의와 대입연산


struct __person
{
char name[30]; // 이름 구조체의 멤버에 배열
char ID[15]; // 주민등록번호 이 올 수 있다.
unsigned int age; // 나이
};
typedef struct __person person; #include <stdio.h>
#include <string.h>
void ShowPersonData(person prsn)
{
ShowPersonData 함수는 구조체 person의
printf("이름: %s \n", prsn.name);
printf("주민등록번호: %s \n", prsn.ID); 데이터를 인자로 전달받아, 그 안에 저장되
printf("나이: %u \n", prsn.age); 어 있는 데이터를 출력하도록 정의
}

int main(void)
{
person jongsoo; 구조체 person의 멤버인 char형 배열
person copyman; 을 초기화 하기 위해 strcpy 함수 사용
strcpy( jongsoo.name, "한종수");
strcpy( jongsoo.ID, "900218-1012589");
jongsoo.age=20;
구조체 변수 jongsoo에 저장된 값
copyman=jongsoo; 을 동일한 자료형 변수인 copyman
ShowPersonData(copyman);
return 0;
에 대입연산을 통해 복사
}
21

구조체 멤버로 선언된 배열의 대입연산과 인자전달


구조체 멤버로
일반적인 배열
선언된 배열
∙ 대입연산을 통한 배열 ∙ 대입연산을 통한 배열
의 복사 불가능 의 복사 가능
∙ 함수의 매개변수로 배열 ∙ 함수의 매개변수로 배열
자체 전달 불가능 자체 전달 가능
22

배열을 멤버로 하는 구조체의 활용 예


#include <string.h>
#define NAME_LEN 30
#define PID_LEN 15

struct __person
{
char name[NAME_LEN]; // 이름
char ID[PID_LEN]; // 주민등록번호
unsigned int age; // 나이
};
typedef struct __person person;

void ShowPersonData(person prsn)


{
printf("이름: %s \n", prsn.name);
printf("주민등록번호: %s \n", prsn.ID);
printf("나이: %u \n", prsn.age);
}

int main(void)
{
person jongsoo;
person copyman;
strcpy( jongsoo.name, "김한양");
strcpy( jongsoo.ID, "970218-2234589");
jongsoo.age=20;
copyman=jongsoo;
ShowPersonData(copyman);
return 0;
}
23

구조체와 구조체 포인터


struct __person
{
char name[30]; // 이름
char ID[15]; // 주민등록번호
1. 구조체 person의 주소 값을 인자로 전달
unsigned int age; // 나이
}; 받도록 함수 정의
typedef struct __person person; 2. 매개변수 ptr은 구조체 변수 copyman을
void ShowPersonData(person * ptr) 가리킴
{
printf("이름: %s \n", (*ptr).name);
1. 포인터를 이용한 구조체 변수의 접근 방식
printf("주민등록번호: %s \n", (*ptr).ID);
printf("나이: %u \n", ptr->age); 2. (*ptr)은 구조체 변수 copyman을 의미
}

int main(void)
{
person jongsoo;
person copyman; 구조체 변수의 포인터
person * personPtr;
선언방식
strcpy( jongsoo.name, "한종수");
strcpy( jongsoo.ID, "900218-1012589");
jongsoo.age=20;

copyman=jongsoo; 구조체 변수의 주소 값을


personPtr=&copyman; 얻을 때도 & 연산자 사용
ShowPersonData(personPtr);
return 0;
}
24

포인터를 이용한 구조체 변수의 접근


접근 방식 1
void ShowPersonData(person * ptr)
{
printf("이름: %s \n", (*ptr).name); ptr이 가리키는 구조체 변수의 name접근
printf("주민등록번호: %s \n", (*ptr).ID); (*ptr).name
printf("나이: %u \n", ptr->age);
ptr이 가리키는 구조체 변수의 ID접근
}
(*ptr).ID
int main(void) ptr이 가리키는 구조체 변수의 age접근
{
.... (*ptr).age

copyman=jongsoo;
personPtr=&copyman;

ShowPersonData(personPtr); 접근 방식 2

ptr이 가리키는 구조체 변수의 name접근


1. 접근 방식 1에서 괄호가 필요한 이유는 ptr->name
. 연산자보다 *연산자의 우선순위가 낮기 ptr이 가리키는 구조체 변수의 ID접근
때문 ptr->ID
2. (*ptr).name과 ptr->name은 완전히 ptr이 가리키는 구조체 변수의 age접근
동일한 연산문 ptr->age
25

포인터를 이용한 구조체 변수의 접근 예


#include <string.h>
#define NAME_LEN 30
#define PID_LEN 15

struct __person
{
char name[NAME_LEN]; // 이름
char ID[PID_LEN]; // 주민등록번호
unsigned int age; // 나이
};
typedef struct __person person;

void ShowPersonData(person * ptr)


{
printf("이름: %s \n", (*ptr).name);
printf("주민등록번호: %s \n", (*ptr).ID);
printf("나이: %u \n", ptr->age);
}

int main(void)
{
person jongsoo;
person copyman;
person * personPtr;
strcpy(jongsoo.name, "한종수");
strcpy(jongsoo.ID, "900218-1012589");
jongsoo.age=20;
copyman=jongsoo;
personPtr=&copyman;
ShowPersonData(personPtr);
return 0;
}
26

구조체 변수의 선언과 초기화


#define NAME_LEN 30 1. 구조체 변수도 일반변수와 마찬가지로
#define PID_LEN 15 선언과 동시에 초기화 가능
struct __person 2. 초기화할 값을 중괄호 안에 쉼표로
{ 구분 지어 표시(배열의 초기화 방법과
char name[NAME_LEN]; // 이름 유사)
char ID[PID_LEN]; // 주민등록번호
unsigned int age; // 나이
};
typedef struct __person person;

void ShowPersonData(person * ptr)


{
printf("이름: %s \n", (*ptr).name);
printf("주민등록번호: %s \n", (*ptr).ID);
printf("나이: %u \n\n", ptr->age);
}

int main(void)
{
person jongsoo={"한종수", "900218-1012589", 20};
person sungeun={"이성은", "910218-1012589", 19};

ShowPersonData(&jongsoo);
ShowPersonData(&sungeun);
return 0;
}
27

구조체의 정의와 typedef 선언을 한번에 끝내세요.

■ 구조체 정의와 typedef 선언의 결합

오른편의 선언방식이 왼편의


선언방식을 대체 할 수 있음
(일종의 약속)
28

구조체의 배열은 구조체 변수로 이뤄진 배열에 지나지 않는다.

■ 구조체 배열
▶ 구조체도 필요에 따라 배열의 형
typedef struct __person
태로 선언 가능 {
char name[30];
▶ 선언방식이 기본 자료형의 배열 char ID[15];
unsigned int age;
선언방식과 동일 } person;

int main(void)
{
int i;
person personArr[3]={
{"한종수", "900218-1012589", 20},
{"이성은", "910218-1012589", 19},
1. person 구조체 배열을 선언과 동시에 {"윤지민", "930218-1012589", 17}
초기화 };
2. 초기화 방식은 각각의 구조체 변수를
....
초기화 할 데이터들의 집합으로 다시
한 번 중괄호로 묶음 return 0;
}
29

구조체 배열의 선언과 활용

#define ARRY_LEN 3 void ShowPersonData(person * ptr)


#define NAME_LEN 30 {
#define PID_LEN 15 printf("이름: %s \n", (*ptr).name);
printf("주민등록번호: %s \n", (*ptr).ID);
typedef struct __person printf("나이: %u \n\n", ptr->age);
{ }
char name[NAME_LEN];
char ID[PID_LEN];
unsigned int age;
} person;

void ShowPersonData(person * ptr);

int main(void)
{
int i;
person personArr[ARRY_LEN]={
{"한종수", "900218-1012589", 20},
{"이성은", "910218-1012589", 19},
{"윤지민", "930218-1012589", 17}
};

for(i=0; i<ARRY_LEN ; i++)


ShowPersonData(&personArr[i]);
return 0;
}
30

구조체 변수도 구조체의 멤버가 될 수 있습니다.


#define PI 3.14 구조체 변수를 멤버로 두고
있는 구조체 변수 cl
typedef struct __point
{
double xPos;
double yPos;
} point; 구조체 point의 변수를
typedef struct __circle 구조체 circle의 멤버로
{ 정의
point center; // 원의중심
double rad; // 반지름
} circle;

void ShowCircleInfo(const circle * ptr)


{
printf("원의중심: [%g, %g] \n", 매개변수 포인터 ptr과 구조체
(ptr->center).xPos, (ptr->center).yPos);
printf("원의넓이: %g \n", 변수 cl과의 참조 관계
(ptr->rad)*(ptr->rad)*PI);
}
구조체 변수를 멤버
int main(void) 로 두고 있는 구조체
{
변수의 초기화 방법
circle cl={
{1.1, 2.2}, // center 초기화
2.5 // 반지름초기화
};
ShowCircleInfo(&cl);
return 0;
31

자기 참조 구조체(Self-Referential Structures)
▶ 자기자신과 동일한 자료형의 구조체 변수를 참조할 수 있도록 정의된 구조체
하나의 box형 구조체 변수가
typedef struct box 다른 box형 구조체 변수의 주
{ 소 값을 저장할 수 있는 형태
int data;
struct box * boxRef;
} box;

int main(void)
{
int i;
box * bxPtr;

box b1={1, NULL};


box b2={11, NULL};
구조체 변수 b1과 b2가
b1.boxRef=&b2; 서로를 참조하는 형태

b2.boxRef=&b1;
....
32

자기 참조 구조체 활용 예
typedef struct box
{ 구조체 box의 멤버로는 구
int data; 조체 box의 포인터 변수가
struct box * boxRef;
} box; 올 수 있다.

int main(void)
{
int i;
box * bxPtr;

box b1={1, NULL};


box b2={11, NULL};

b1.boxRef=&b2;
b2.boxRef=&b1;

bxPtr=&b1;

for(i=1; i<=10; i++)


{
printf("%3d", bxPtr->data);
(bxPtr->data)++;
bxPtr=bxPtr->boxRef;
if(!(i%2))
printf("\t");
}
return 0;
}
33

공용체
■ 공용체(union)
▶ 같은 메모리 영역을 여러 개의 변수가 공유
▶ 공용체를 선언하고 사용하는 방법은 구조체와 아주 비슷

union example {
char c; // 같은 공간 공유
int i; // 같은 공간 공유
};
34

공용체

#include <stdio.h>

union example {
공용체 선언
int i;
char c;
};
공용체 변수 선언.
int main(void)
{ char 형으로 참조.
union example v;

v.c = 'A';
printf("v.c:%c v.i:%i\n", v.c, v.i );

v.i = 10000; int 형으로 참조.


printf("v.c:%c v.i:%i\n", v.c, v.i);

v.c:A v.i:-858993599
v.c:† v.i:10000
35

공용체에 타입 필드 사용

#include <stdio.h>
#include <string.h>
#define STU_NUMBER 1
#define REG_NUMBER 2

struct student {
int type;
union {
int stu_number; // 학번
char reg_number[15]; // 주민등록번호
} id;
char name[20];
};
36

공용체에 타입 필드 사용

void print(struct student s)


{
switch(s.type)
{
case STU_NUMBER:
printf("학번 %d\n", s.id.stu_number);
printf("이름: %s\n", s.name);
break;
case REG_NUMBER:
printf("주민등록번호: %s\n", s.id.reg_number);
printf("이름: %s\n", s.name);
break;
default:
printf("타입오류\n");
break;
}
}
37

공용체에 타입 필드 사용
학번: 20190001
이름: 홍길동
주민등록번호: 860101-1056076
int main(void) 이름: 김철수
{
struct student s1, s2;

s1.type = STU_NUMBER;
s1.id.stu_number = 20190001;
strcpy(s1.name, "홍길동");

s2.type = REG_NUMBER;
strcpy(s2.id.reg_number, "860101-1056076");
strcpy(s2.name, "김철수");

print(s1);
print(s2);
}
38

열거형
■ 열거형(enumeration)이란 변수가 가질 수 있는 값들을 미리 열거해
놓은 자료형
■ (예) 요일을 저장하고 있는 변수는 { 일요일, 월요일, 화요일, 수요일,
목요일, 금요일, 토요일 } 중의 하나의 값만 가질 수 있다.
39

열거형 선언

열거형 변수 선언
enum days today;
today = SUN; // OK!
40

열거형의 필요성
■ 다음과 같이 프로그램을 작성할 수 있다.
▶ int today;
▶ today = 0; // 일요일
▶ today = 1; // 월요일

■ 오류를 줄이고 가독성을 높여야 된다.


■ 0보다는 SUN라는 기호상수가 더 바람직하다. 의미를 쉽게 알 수 있기
때문이다.
■ today에 9와 같은 의미없는 값이 대입되지 않도록 미리 차단하는
것도 필요하다.
41

열거형 초기화
• 값을 지정하기
않으면 0부터
할당

enum days { SUN, MON, TUE, WED, THU, FRI, SAT }; // SUN=0, MON=1, ...
enum days { SUN=1, MON, TUE, WED, THU, FRI, SAT }; // SUN=1, MON=2, ...
enum days { SUN=7, MON=1, TUE, WED, THU, FRI, SAT=6 };// SUN=7, MON=1, ...

ex)
enum colors { white, red, blue, green, black };
enum boolean { false, true };
enum levels { low, medium, high };
enum car_types { sedan, suv, sports_car, van, pickup, convertible };
예제
#include <stdio.h>
enum days { SUN, MON, TUE, WED, THU, FRI, SAT };
char *days_name[] = {
"sunday", "monday", "tuesday", "wednesday", "thursday", "friday",
"saturday" };

int main(void)
{
enum days d;
d = WED;
printf("%d번째 요일은 %s입니다\n", d, days_name[d]);
return 0;
}

3번째 요일은 wednesday입니다


43

열거형과 다른 방법과의 비교

정수 사용 기호 상수 열거형
#define LCD 1 enum tvtype { LCD, OLED };
#define OLED 2 enum tvtype code;
switch(code) {
case 1:
switch(code) { switch(code) {
printf("LCD TV\n");
case LCD: case LCD:
break;
printf("LCD TV\n"); printf("LCD TV\n");
case 2:
break; break;
printf(“OLED TV\n");
case OLED: case PDP:
break;
printf(“OLED TV\n"); printf(“OLED TV\n");
}
break; break;
} }
컴퓨터는 알기 쉬우나 사람은 기호 상수를 작성할 때 오류를 컴파일러가 중복이 일어나지 않
기억하기 어렵다. 저지를 수 있다. 도록 체크한다.

You might also like