540 likes | 745 Views
처음으로 배우는 C 프로그래밍. 제 4 부 복합 데이터 형. 제 10 장 구조체. 구조체. 집단을 구성하는 개개 원소들이 어떻게 배치되어 있는지 혹은 어떻게 조직화되어 있는지를 나타냄 예 : 회사 정보 : 회사원 정보와 부서 정보 정부의 조직 : 조직원 , 조직의 형태 , 조직의 배치 정보 개 개의 자료 항목이 어떻게 배치되어 있는지를 잘 설명하고 있는 단위 (unit) 예 ( 우편물 주소 ) Name: ( 자료 항목 ) - 개체 (entity)
E N D
처음으로 배우는 C 프로그래밍 제4부 복합 데이터 형 제 10 장 구조체
구조체 • 집단을 구성하는 개개 원소들이 어떻게 배치되어 있는지 혹은 어떻게 조직화되어 있는지를 나타냄 • 예: • 회사 정보: 회사원 정보와 부서 정보 • 정부의 조직: 조직원, 조직의 형태, 조직의 배치 정보 • 개 개의 자료 항목이 어떻게 배치되어 있는지를 잘 설명하고 있는 단위(unit) • 예 (우편물 주소) Name: (자료 항목) - 개체(entity) Street Address: (자료 항목) - 개체(entity) City: (자료 항목) - 개체(entity) State: (자료 항목) - 개체(entity) Zip Code: (자료 항목) - 개체(entity) • 레코드(record): 개개의 자료 항목을 모아 형성한 더 큰 집단 => 구조체 • 구조체 형태: 개개의 자료 항목의 기호명, 자료형과 배치로 구성 • 구조체 내용: 기호명에 저장된 실 자료를 의미
구조체(계속) • 구조체 형태: 개개의 자료 항목의 기호명, 자료형과 배치로 구성 Name Street Address City State Zip Code • 구조체 내용: 기호명에 저장된 실 자료를 의미 Rhona Bronson-Karp 614 Freeman Street Orange NJ 07052
단일 구조체 • 구조체의 선언 • 자료형, 자료 명, 자료 항목 배치 작성 • 구조체 정의 예 (년월일을 저장하는 구조체) struct { int month; /* 월 */ int day; /* 일*/ int year; /* 년*/ } birth; • 구조체 멤버 접근 방법(연산자 .) • birth.month (birth 구조체 멤버 month 접근) • birth.year (birth 구조체 멤버 year 접근) 자료 항목, 구조체 멤버 구조체 변수
#include <stdio.h> void main(void) { struct { int month; int day; int year; } birth; birth.month = 10; birth.day = 12; birth.year = 63; printf("My birth date is %d/%d/%d.", birth.month, birth.day, birth.year); } 출력 결과 My birth date is 10/12/63.
구조체 정의 • 변수 명을 갖는 구조체 정의 • 단일 변수 • struct {int month; int day; int year;} birth; • 여러 변수 • struct {int month; int day; int year;} birth, current; birth.month, birth.day, birth.year current.month, current.day, current.year
구조체 정의 (계속) • 변수명이 없는 정의 struct Date { /* Date <- 구조체 태그(tag) 명, 구조체 이름 */ int month; int day; int year; }; 구조체 템플리트(template) 구조체 템플리트를 이용한 변수 선언 struct Date birth, current; 멤버 접근 birth.month, birth.day, birth.year current.month, current.day, current.year
#include <stdio.h> struct Date { int month; int day; int year; }; void main(void) { (1) struct Date birth; (2) birth.month = 10; (3) birth.day = 12; (4) birth.year = 63; printf("My birth date is %d/%d/%d", birth.month, birth.day, birth.year); } 구조체 템플리트 선언 구조체 멤버의 초기화 struct Date birth = {10, 12, 63};
구조체 선언 예 회사원 정보 Name Identification Number Regular Pay Rate Overtime Pay Rate 회사원 정보를 위한 구조체 템플리트선언 struct Pay_rec { char name[20]; int id_num; float reg_rate; float ot_rate; } ; 구조체 정의와 초기화 struct Pay_rec employee = {"H. Price", 12387, 15.89, 25.50};
사람의 생년월일을 위한 구조체 • Date 구조체 사용 struct { char name[20]; /* 이름을 저장하는 변수 */ struct Date birth; /* 생년월일을 저장하는 변수 */ } person; • 멤버 접근 방법 • person.name • person.birth.month • person.birth.day • person.birth.year
구조체 메모리 할당 • 구조체 템플리트(틀) 정의는 구조체의 모양을 정의하며 그 형식은 다음과 같음 struct tag_name{ type1 member1; type2 member2; : typen membern; }; • x-y 좌표 상에서 “하나의 점과 그 이름을 표시하는 구조체”의 예는 다음과 같음 예) struct aPoint { int x; int y; char name[10]; }; tag_name : 구조체의 이름 type1, … typen : 각 멤버의 자료형 member1, … membern : 각 멤버의 이름 Name[1] aPoint Name[9] Name[0] x y 구조체에 대한 메모리 할당
pt1 (10, 20, 청주) 10 20 청주 (10, 15, 대전) 구조체 템플리트 정의와 변수선언 • 구조체 템플리트 정의와 구조체 변수 선언을 한 번에 할 수 있음 struct tag_name { type1 member1; type2 member2; : typen membern; } struct_variables, ... ; 예) struct aPoint { int x; int y; char name[5]; } pt1, pt2, pt3;
구조체 배열 • 사원에 대한 자료 사원번호 사원이름 사원급여지급비율 ========================================== 32479 Abrams, B. 6.72 <= 첫 번째 레코드 33623 Bohm, P. 7.54 <= 두 번째 레코드 34145 Donaldson, S. 5.56 <= 세 번째 레코드 35987 Ernst, T. 5.43 <= 네 번째 레코드 36203 Gwodz, K. 8.72 <= 다섯 번째 레코드 36417 Hanson, H. 7.64 <= 여섯 번째 레코드 37634 Monroe, G. 5.29 <= 일곱 번째 레코드 38321 Price, S. 9.67 <= 여덟 번째 레코드 39435 Robbins, L. 8.50 <= 아홉 번째 레코드 39567 Willians, B. 7.20 <= 열 번째 레코드 정수형 배열 문자형 배열 실수형
구조체 배열(계속) • 사원을 위한 구조체 • struct Pay_rec { long idnum; char name[20]; float rate;}; • 사원들의 정보를 저장하기 위한 구조체 배열 • struct Pay_rec empolyee[10]; employee[0].rate : 첫번째 사원의 급여 지급 비율 employee[4].idnum : 다섯번째 사원의 사원 번호
#include <stdio.h> struct Pay_rec { long id; char name[20]; float rate; }; /* 전역 템플리트 구성 */ void main(void) { int i; struct Pay_rec employee[10] = { 32479, "Abrams, B.", 6.72, 33623, "Bohm, P.", 7.54, 34145, "Donaldson, S.", 5.56, 35987, "Ernst, T.", 5.43, 36203, "Gwodz, K.", 8.72, 36417, "Hanson, H.", 7.64, 37634, "Monroe, G.", 5.29, 38321, "Price, S.", 9.67, 39435, "Robbins, L.", 8.50, 39567, "Willians, B.", 7.20 }; for (i=0; i<5; i++) printf("\f2 nld %-20s %4.2f", employee[i].id, employee[i].name, employee[i].rate); } /* end of main */ 출력결과 32479 Abrams, B. 6.72 33623 Bohm, P. 7.54 34145 Donaldson, S. 5.56 35987 Ernst, T. 5.43 36203 Gwodz, K. 8.72
구조체 전달 • 구조체 멤버의 전달 • 스칼라(scalar) 변수와 같은 방법으로 전달 • 구조체 전달(값에 의해) struct { int id_num; double pay_rate; double hours; } emp; display(emp.id_num); calc_pay(emp.pay_rate, emp.hours); calc_net(emp);
#include <stdio.h> struct Employee { int id_num; double pay_rate; double hours; }; void main(void) { struct Employee emp = 6782, 8.93, 40.5; double net_pay; double c_net(struct Employee); net_pay = calc_net(emp); /* emp로 값의 복사본 전달 */ printf("The net pay for employee %d is $%6.2f", emp.id_num, net_pay); } double calc_net(struct Employee temp) { return (temp.pay_rate * temp.hours); } 출력결과 The net pay for employee 6782 is $361.66
구조체 전달(계속) 구조체 전달(주소에 의한) 피호출문 void calc_net(struct Employee *pt) { /*pt: 구조체 포인터 변수 */ } 호출문 calc_net(&emp);
구조체 타입의 포인터 변수 예) struct Employee *pt1, *pt2, *pt3; • 포인터 변수로부터 구조체의 멤버를 참조하는 방법 • 포인터의 성질을 이용한 방법 (*pt1).hours • ‘->’연산자를 이용한 방식 pt1 -> hours • ‘->’연산 • ‘->’연산자의 왼쪽은 구조체의 주소, 오른쪽은 그의 멤버이름을 넣어야 함 • 구조체 연산자 . 와 -> 는 함수의 ( )와 배열 첨자의 [ ]와 함께 연산 순위가 가장 높음 • 그러므로 *point.hours는 *(point.hours)와 같은 효과를 지니고, ++pt->hours는 ++(pt-> hours)와 같은 효과를 지닌다.
*pt.hours = *(pt.hours) <= 프로그램 오류 (*pt) *pt == (*pt).id_num (*pt).id_num == pt->id_num (*pt).pay_rate == pt->pay_rate (*pt).hours == pt->hours (*pointer).member => pointer->member
주소에 의해 구조체 전달 예 #include <stdio.h> struct Employee { int id_num; double pay_rate; double hours; }; void main(void) { struct Employee emp = 6782, 8.93, 40.5; double net_pay, double c_net(struct Employee *); net_pay = calc_net(&emp); /* emp로 값의 복사본 전달 */ printf("The net pay for employee %d is $%6.2f", emp.id_num, net_pay); } double calc_net(struct Employee *temp) { return (temp->pay_rate * temp->hours); }
구조체 포인터 연산 pt++; ++pt; pt--; --pt; ++pt->hours;
구조체 반환 • 함수의 반환 값으로 구조체를 받기를 원하는 경우 스칼라(scalar) 값을 반환할 때와 같은 방법으로 구조체를 반환 #include <stdio.h> struct Employee { int id_num; double pay_rate; double hours; }; struct Employee get_vals(void) { struct Employee em; …. return em; } 구조체반환 void main() { struct Employee emp; ... emp = get_vals(); /*함수 호출*/ } emp.id_num = em.id_num; emp.pay_rate = em.pay_rate; emp.hours = em.hours
#include <stdio.h> struct Employee { int id_num; double pay_rate; double hours; }; void main(void) { struct Employee emp; struct Employee get_vals(void); emp = get_vals(); printf("\f2 nThe employee id number is %d", emp.id_num); printf("The employee pay rate is $%5.2f", emp.pay_rate); printf("The employee hours are %5.2f", emp.hours); } struct Employee get_vals(void) { struct Employee new_emp; new_emp.id_num = 6789; new_emp.pay_rate = 16.25; new_emp.hours = 38.0; return (new_emp); } 출력결과 The employee id number is 6789 The employee pay rate is $16.25 The employee hours are 38.00
연결 리스트(Linked List) • 자료 처리 방법 • 특정 순서로 존재하는 레코드(record)에 새로운 레코드를 삽입하거나 기존의 레코드를 삭제할 때 존재하는 레코드가 계속적으로 그 순서를 유지하도록 함 • 예 (알파벳순서로 정렬된 전화번호 목록) Acme, Sam (201) 898-2392 Dolan, Edith (213) 682-3104 Lanfrank, John (415) 718-4581 Mening, Stephanie (914) 382-7070 Zemann, Harold (718) 219-9912
Mening, Stephanie (914) 382-7070 Acme, Sam (201) 898-2392 Lanfrank, John (415) 718-4581 Zemann, Harold (718) 219-9912 Dolan, Edith (213) 682-3104 연결리스트(계속) • 배열 구조를 사용하는 경우 struct person { char name[20]; char telnum[20]; }; 삽입과 삭제의 문제점 발생 struct person hb[10]; hb[0] hb[1] hb[2] hb[3] hb[4]
연결 리스트(계속) • 연결 리스트는 간단한 구조체 집합으로 각 구조체는 리스트 내에서 논리적으로 순서화된 구조체 주소를 갖는 적어도 하나의 멤버를 포함 • 물리적 측면:물리적으로 적절한 순서로 저장되기보다는 오히려 새로운 레코드가 리스트에 추가될 때 물리적으로 컴퓨터의 저장장소내 사용 가능한 임의의 공간에 추가됨 • 논리적 측면:레코드들은 자기 앞의 레코드가 자신의 주소를 가리키게 함으로써 서로 연결됨 • 프로그래밍 관점: 처리될 현재의 레코드는 다음 레코드가 물리적으로 어디에 저장되어 있든지 무관하게 다음 레코드의 주소를 포함함 구조체 포인터
연결 리스트(계속) • 연결 리스트 삽입 (June Hagar 자료 삽입)
연결 리스트(계속) • 연결리스트(삭제)
연결 리스트(계속) • 구조체에 구조체 포인터 추가 #include <stdio.h> struct Test { int id_num; double *pt_pay; }; void main(void) { struct Test emp; double pay = 456.20; emp.id_num = 12345; emp.pt_pay = &pay; printf("Employee number %d was paid $%6.2f'', emp.id_num, *emp.pt_pay); } 출력결과 Employee number 12345 was paid $456.20
연결 리스트(계속) • 전화번호 저장을 위한 연결 리스트 구조체 #include <stdio.h> struct Tele_typ { char name[30]; char phone_no[15]; struct Tele_typ *nextaddr; }; 구조체 포인터
void main(void) { struct Tele_typ t1 = ''Acme, Sam'', ''(201) 898-2392''; struct Tele_typ t2 = ''Dolan, Edith'', ''(213) 682-3104''; struct Tele_typ t3 = ''Lanfrank, John'', ''(415) 718-4581''; Tele_typ *first; /* 구조체 포인터 생성 */ first = &t1; /* t1 주소를 first 에 저장 */ t1.nextaddr = &t2; /* t2 주소를 t1.nextaddr에 저장 */ t2.nextaddr = &t3; /* t3 주소를 t2.nextaddr에 저장 */ t3.nextaddr = NULL; /* NULL 를 t3.nextaddr에 저장 */ printf(''%s %s %s'', first->name, t1.nextaddr->name, t2.nextaddr->name); } 출력결과 Acme, Sam Dolan, Edith Lanfrank, John
구조체를 포인터로 전달 void main(void) { struct Tele_typ t1 = "Acme, Sam", "(201) 898-2392"; struct Tele_typ t2 = "Dolan, Edith", "(213) 682-3104"; struct Tele_typ t3 = "Lanfrank, John", "(415) 718-4581"; Tele_typ *first; /* 구조체 포인터 생성 */ first = &t1; /* t1 주소를 first 에 저장 */ t1.nextaddr = &t2; /* t2 주소를 t1.nextaddr에 저장 */ t2.nextaddr = &t3; /* t3 주소를 t2.nextaddr에 저장 */ t3.nextaddr = NULL; /* NULL 를 t3.nextaddr에 저장 */ display(first); /* 구조체 first의 주소를 전달 */ } #include <stdio.h> struct Tele_typ{ char name[30]; char phone_no[15]; struct Tele_typ *nextaddr; };
void display(struct Tele_typ *contents) /*contents는 Tele_typ 형 구조체의 포인터 변수임 */ { while (contents != NULL) /* 연결 리스트에 저장된 모든 구조체 정보를 출력 */ { printf("%30s %-20s %s", contents->name, contents->phone_no); contents = contents->nextaddr; } return; } 출력결과 Acme, Sam (201) 898-2392 Dolan, Edith (213) 682-3104 Lanfrank, John (415) 718-4581
동적 메모리 할당 • 프로그램에서 정의된 모든 변수는 그 변수의 값을 저장하기에 충분한 기억장소를 컴퓨터 메모리로부터 할당 받음 • 특정 메모리 위치가 변수를 위해 확보되면, 변수의 유효 범위(life time)내에서는 그 위치가 사용되든 그렇지 않든 그 위치는 변하지 않는다 • 예:500 개의 정수를 저장하는 배열 사용하는 정수: 10개, 490개의 기억 공간 낭비 => 효율적인 기억 장소 필요성 • 필요할 때 필요한 만큼 기억장소를 확보 • 불필요한 기억장소는 해제 • 예: 연결 리스트(삽입 정보, 정보 삭제)
동적 메모리 할당 함수 동적 메모리 할당을 위한 include 함수명: stdlib.h 메모리 할당 함수 - 함수명: malloc(int ) - 기능: 요구된 바이트 수() 만큼 기억장소를 확보한다. 성공적으로 기억장소를 확보하면 기억장소의 시작 주소를 반환하고 충분한 메모리가 없는 경우 NULL를 반환한다. -예: int *pt; pt = malloc(200 * sizeof(int)); 메모리 해제 함수 -함수명: free() -기능:미리 확보된 바이트의 블록을 해제한다. 해제될 기억장소의 시작 주소가 이 함수의 인자로 사용된다. -예: free(pt);
배열의 크기를 입력 받아 그 만큼의 배열을 위한 기억 장소 할당 int numgrades; int *grades; /* 정수형 포인터 변수 정의 */ printf("Enter the number of grades to be processed: "); scanf("%d", &numgrades); /* 요구된 만큼의 정수형 배열을 위한 기억장소 할당 */ grades = (int *) malloc(numgrades * sizeof(int));
#include <stdio.h> #include <stdlib.h> void main(void) { int numgrades; int *grades; /* 정수형 포인터 변수 정의 */ printf("Enter the number of grades to be processed: "); scanf("%d", &numgrades); /* 요구된 만큼의 정수형 배열을 위한 기억장소 할당 */ grades = (int *) malloc(numgrades * sizeof(int)); if (grades == (int *) NULL) {/* 동적기억장소할당이 정상적으로 이루어졌는지를 검사 */ printf("Failed to allocate grades array "); exit(1); }
for (i=0; i < numgrades; i++) { printf(" Enter a grade: "); scanf("%d", &grades[i]); } printf("\f2 nAn array was created for %d integers", numgrades); printf("\f2 nThe vales stored in the array are:"); for (i=0; i < numgrades; i++) printf(" %d", grades[i]); free(grades); } 실행결과 Enter the number of grades to be processed: 4 Enter a grade: 85 Enter a grade: 96 Enter a grade: 77 Enter a grade: 92 An array was created for 4 integers The value stored in the array are: 85 96 77 92
구조체 동적 기억장소 할당 struct Office_info { 자료 멤버들; }; struct Office_info *off; /*할당된 주소를 저장하기 위한 포인터 변수 */ /* 구조체를 저장하기 위한 공간 확보 */ off = (struct Office_info *) malloc(sizeof(struct Office_info)); /* 공간이 할당되었는지를 점검 */ if (off == (struct Office_info *) NULL){ printf("\f2 nAllocation of Office info record failed"); exit(1) }
동적 연결 리스트 초기 리스트
동적 연결 리스트 삽입 예 Carter 삽입
동적 연결 리스트 삽입 방법 INSERT (새로운 레코드를 연결 리스트에 추가) { -새로운 레코드를 위한 공간을 동적으로 할당 if 리스트에 임의의 레코드가 존재하지 않음 -새로운 레코드의 주소 필드를 NULL로 지정 -첫 번째 레코드 포인터를 새롭게 생성된 레코드의 주소로 지정 else -새로운 레코드가 리스트의 어느 위치에 놓여져야 할 것인지를 결정 if 레코드가 리스트의 첫 번째 레코드로 존재해야 함 -첫 번째 레코드 포인터의 값을 새롭게 추가된 레코드의 주소 필드에 복사 -첫 번째 레코드 포인터의 주소를 새롭게 추가된 레코드의 주소로 지정 else -추가된 레코드의 바로 앞에 놓일 레코드의 포인터 멤버의 값을 새롭게 추가된 레코드의 주소 필드에 복사 -추가된 레코드의 바로 앞에 놓일 레코드의 포인터 멤버의 값을 새롭게 추가된 레코드의 주소로 지정 /* endif */ /* endif } /* end of INSERT
동적 연결 리스트 삽입 새로운 레코드의 선형 탐색(LINEAR LOCATION of a NEW RECORD) if 새로운 레코드의 키이 필드가 첫 번째 레코드 키이 필드보다 작으면 - 새로운 레코드가 첫 번째 레코드임 else while (리스트에 레코드가 있음) { - 새로운 레코드의 키이 값과 각 레코드의 키이 값과 비교 - 새로운 레코드 키가 두 개의 레코드사이에 존재하거나 혹은 리스트의 끝에 속하는 경우 비교를 멈춤 } endwhile endif
레코드 삽입 예 #include <stdio.h> #include <stdlib.h> #define MAXCHARS 30 /* 연결 리스트 레코드의 선언 */ struct Name_rec { char name[MAXCHARS]; struct Name_rec *next_addr; }; /* 첫 번째 레코드 포인터의 정의 */ struct Name_rec *first_rec; void main(void) { void read_insert(void); /* 함수 프로토타입 */ void display(void); first_rec = NULL; /* 리스트 포인터 초기화 */ read_insert(); display(); }
/* 이름을 입력받아 이를 연결 리스트에 삽입 */ void read_insert(void) { char name[MAXCHARS]; void insert(char []); printf("\f2 nEnter as many names as you wish, one per line"); printf("\f2 nTo stop entering names, enter a single x "); while(1) { printf("Enter a name: "); gets(name); if (strcmp(name, "x") == 0) break; insert(name); } }
void insert(char *name) { struct Name_rec *locate(char *); /* 함수 프로토타입 */ struct Name_rec *newaddr, *here; /* Name_rec 형 구조체 포인터 */ newaddr = (struct Name_rec *) malloc(sizeof(struct Name_rec)); if (newaddr == (struct Name_rec *) NULL) { /* 주소를 점검 */ printf("\f2 nCould not allocate the requested space"); exit(1); } /* 새로운 레코드가 어디에 놓여져야 할지를 결정, 모든 포인터 멤버를 갱신 */ if (first_rec == NULL) {/* 현재 리스트에 존재하는 레코드가 없는 경우 */ newaddr->next_addr = NULL; first_rec = newaddr; } else if (strcmp(name, first_rec->name) < 0) { /* 리스트에 첫 번째 레코드로 추가되는경우 */ newaddr->next_addr = first_rec; first_rec = newaddr; } else {/* 레코드가 리스트의 첫 번째 레코드가 아닌 경우 */ here = locate(name); newaddr->next_addr = here->next_addr; here->next_addr = newaddr; } strcpy(newaddr->name, name); }
/* 존재하는 연결 리스트에 새로 추가될 레코드의 삽입될 위치를 결정 */ struct Name_rec *locate(char *name) { struct Name_rec *one, *two; one = first_rec; two = one->next_addr; if (two == NULL) return (one); /* 새로운 레코드는 존재하는 단일 레코드 다음에 삽입될 것임 */ while(1) { if (strcmp(name, two->name) <0) /* 리스트내의 이 위치에 레코드를 삽입한다 */ break; else if (two->next_addr == NULL) { /* 마지막 레코드 다음에 위치 */ one = two; break; } else { /* 계속적으로 삽입될 위치를 찾음 */ one = two; two = one->next_addr; } return (one); }
/* 연결 리스트로부터 name을 출력 */ void display(void) { struct Name_rec *contents; contents = first_rec; printf("\f2 nThe names currently on the list are:"); while (contents != NULL) { printf("%s", contents->name); contents = contents->next_addr; } return; } 출력결과 Enter as many names as you wish, one per line To stop entering names, enter a single x Enter a name: Binstock Enter a name: Arnold Enter a name: Duberry Enter a name: Carter Enter a name: x The names currently on the list are: Arnold Binstock Carter Duberry