9 장구조체와공용체 박종혁교수 UCS Lab Tel: 970-6702 Email: jhpark1@seoultech.ac.kr SeoulTech 2017-1 st 프로그래밍입문 (1)
2 구조체와공용체 C 언어의확장방법 매크로와라이브러리 사용자정의자료형 ( 배열, 구조체, 공용체 ) // 파생형자료형으로쓰기도함
구조체의필요성 int number; char name[10]; double grade; 구조체를사용하면변수들을하나로묶을수있습니다.
구조체와배열 구조체 vs 배열 같은타입의집합 다른타입의집합
5 구조체의장점 통계자료나성적등과같이관련있는서로다른자료형을한덩어리 ( 집합 ) 로만들어처리 연관있는데이터를한덩어리로묶으면프로그램의관리와구현이용이 순서대로정렬 (sort) 하는경우구조체단위로처리되므로간단 네트워크프로그램을작성할때소켓이나헤더 (header) 의 format( 형식 ) 을구조체로묶어서처리 함수를반환할때한개의데이터가아닌구조체단위로묶어서전달할수있음
6 구조체 서로다른형의변수들을하나로묶어주는방법 예제 - 카드
7 구조체 예제 - 카드 각카드는고유의무늬와숫자를가짐 구조체를사용하여표현하면효율적 카드를위한구조체선언 struct card { int pips; char suit; };
8 구조체선언 예제 - 카드 struct card { int pips; char suit; }; struct : 키워드 card : 구조체태그이름 pips, suit : 구조체멤버 * 이것은 struct card 형의정의이고, 변수선언은아님
9 구조체변수선언방법 1 struct card 형변수 c1, c2 struct card { int pips; char suit; }; struct card c1, c2;
10 구조체변수선언방법 2 struct card 형변수 c1, c2 struct card { int pips; char suit; } c1, c2;
11 구조체변수선언방법 3 struct card 형변수 c1, c2 struct card { int pips; char suit; }; typedef struct card card c1, c2; card;
12 구조체변수선언방법 4 struct card 형변수 c1, c2 typedef struct { int pips; char suit; } card; card c1, c2;
13 구조체변수선언방법 5 struct 형변수선언 struct { int pips; char suit; } c1, c2; 구조체태그이름이없음에주의 C1, c2 와같은형의변수는다시는선언할수없음
14 구조체변수선언방법 struct { int pips; char suit; } c1, c2; struct { int pips; char suit; } c3, c4; * c1, c2 는 c3, c4 와는다른형임
멤버접근연산자 (*p).number p 가가리키는구조체변수 == p->number p 가가리키는구조체변수의멤버 number p 가가리키는구조체변수의멤버 number
16 멤버접근연산자. 일반구조체멤버접근연산자 c1.pips = 3; c1.suit = 's'; c2.pips = c1.pips; c2.suit = c1.suit;
17 멤버접근연산자 -> 포인터를통한구조체멤버접근연산자 pointer_to_structure -> member_name 다른방법 (*pointer_to_structure).member_name
18 멤버접근연산자 -> 예제 struct complex { double re; /* real part */ double im; /* imag part */ }; typedef struct complex complex; void add(complex *a, complex *b, complex *c) { a -> re = b -> re + c -> re; a -> im = b -> im + c -> im; }
19 멤버접근연산자 선언문과배정문 struct student tmp, *p = &tmp; tmp.grade = 'A'; tmp.last_name = "Casanova"; tmp.student_id = 910017 수식동등한수식개념적값 tmp.grade tmp.last_name (*p).student_id *p -> last_name + 1 *(p -> last_name + 2) p -> grade p -> last_name p -> student_id (*(p -> last_name)) + 1 (p -> last_name)[2] A Casanova 910017 D s
20 구조체멤버 한구조체내에서멤버이름은유일해야하나, 서로다른구조체에서는같은멤버이름을사용할수있음 struct fruit { char *name; int calories; }; struct vegetable { char *name; int calories; }; struct fruit a; struct vegetable b;
21 구조체멤버 구조체의멤버로는배열, 포인터변수, 이미정의된다른구조체의변수등모든응용자료형을사용할수있다. 멤버로배열을사용하는예 struct profile{ int age; double height; char name[20]; }; struct profile pf; // 나이를저장할멤버 // 키를저장할멤버 // 이름을저장할멤버 // 구조체변수의선언 name 멤버의사용 strcpy(pf.name, 홍길동 ); printf( %s\n, pf.name);
22 구조체멤버 멤버로포인터변수를사용하는예 struct profile{ int age; double height; char *np; }; struct profile pf; pf.np= 홍길동 ; // 나이를저장할멤버 // 키를저장할멤버 // 이름을연결할포인터변수멤버 // 구조체변수의선언 // 포인터변수멤버에문자열연결 - 포인터변수를멤버로사용하는경우키보드로부터문자열입력은 불가능하다 ( 문자열을저장할기억공간이없다!).
23 구조체멤버 구조체의멤버로다른구조체의변수를사용하는예 struct student{ struct profile pf; int num; double grade; }; struct student s1; // 이미선언된구조체를멤버로사용 // 구조체변수의선언 - 구조체의멤버로구조체를사용한경우멤버참조연산자를두번사용 하여멤버를참조해야한다.
24 구조체멤버 #include <stdio.h> struct profile{ int age; double height; char *np; }; struct student{ struct profile pf; int num; double grade; }; int main() { struct student s1; s1.pf.age=23; s1.pf.height=187.5; s1.pf.np=" 홍길동 "; s1.num=5; s1.grade=4.4; } printf(" 이름 : %s\n", s1.pf.np); printf(" 나이 : %d\n", s1.pf.age); printf(" 키 : %.1lf\n", s1.pf.height); printf(" 학번 : %d\n", s1.num); printf(" 학점 : %.1lf\n", s1.grade); return 0;
25 멤버접근연산자. 배열은배열요소의형태가같으므로주소계산에의해각멤버의참조가가능 하지만구조체는각멤버의형태가다르므로멤버참조연산자 (.) 로직접멤버 를참조해야한다. #include <stdio.h> struct student{ int num; double grade; }; int main() { struct student s1; // 구조체변수선언 } s1.num=2; // 구조체멤버참조 s1.grade=2.7; printf( 학번 : %d\n, s1.num); printf( 학점 : %.1lf\n, s1.grade); return 0;
26 멤버접근연산자 -> 포인터변수로멤버를참조하기전에먼저구조체변수를참조한다. - 멤버참조연산자 (.) 가참조연산자 (*) 보다우선순위가높으므로괄호가필요하다. #include <stdio.h> struct score{ int kor, eng, mat; }; int main() { struct score a={90, 80, 70}; struct score *sp=&a; } // 구조체의형틀선언 printf( 국어 : %d\n, (*sp).kor); printf( 영어 : %d\n, (*sp).eng); printf( 수학 : %d\n, (*sp).mat); return 0; // 구조체변수의선언과초기화 // 포인터변수에포인터저장 // 포인터변수로구조체변수의 // 각멤버를참조하여출력한다.
27 멤버접근연산자 -> 포인터변수가가리키는구조체변수의멤버를간단히참조할때간 접멤버참조연산자 (->) 를사용한다. - list 구조체배열의데이터를출력하는함수를만들자. struct address list[5] = { }; list_prn(list); // list 는구조체배열의배열명 // 배열명을주고함수를호출한다.
28 멤버접근연산자 -> 구조체배열의배열명은첫번째구조체를가리키는포인터이므로 배열명을받는매개변수는구조체포인터변수이어야한다. void list_prn(struct address *lp) { int i; } for(i=0; i<5; i++){ printf( %10s%5d%15s%20s\n, lp[i].name, lp[i].age, lp[i].tel, lp[i].addr); } 매개변수 lp 가구조체포인터변수이므로간접멤버참조연산자로 멤버를참조할수있다. // 배열표현을포인터표현으로바꾼다. // 간접멤버참조연산자를사용한다.
29 함수에서구조체 구조체는함수의인자로써함수에전달될수있고, 함수로부터리턴될수도있음 함수의인자로서구조체가전달될때구조체는값으로전달됨 구조체가많은멤버를가지거나, 큰배열을멤버로가질경우, 함수의인자로구조체를전달하는것은상대적으로비효율적임 따라서대부분의응용프로그램에서는함수의인자로구조체의주소를사용함
30 함수에서구조체예제 typedef struct { char int struct dept struct home_address double... name[25]; employee_id; department; *a_ptr; salary; } employee_data; * department 멤버는그자체가구조체이고, 컴파일러는각멤버의크기를미리알아야하므로 struct dept에대한선언이먼저와야함
31 함수에서구조체예제 함수선언방법 employee_data update(employee_data e){... printf("input the department number: "); scanf("%d", &n); e.department.dept_no = n;... return e; } * 함수호출 employee_data e; e = update(e);
32 함수에서구조체예제 함수선언방법 Void update(employee_data *p) {... printf("input the department number: "); } scanf("%d", &n); p->department.dept_no = n;... * 함수호출 employee_data e; update(&e);
33 구조체의초기화 struct 태그이름구조체변수 = { 데이터 1, 데이터 2,... }; struct Person { char name[11]; /* 이름 */ char jumin[15]; /* 주민등록번호 */ int age; /* 나이 */ double weight; /* 몸무게 */ }; struct Person kang = {" 강한자, 123456-1234567, 20, 57.59};
34 구조체변수도배열과같이중괄호를사용하여초기화한다. - profile 구조체변수를초기화하는예 struct profile{ int age; double height; char name[20]; }; // 나이를저장할멤버 // 키를저장할멤버 // 이름을저장할멤버 구조체의형틀선언, 변수선언, 초기화를동시에할수있다.
35 구조체의초기화 - 예제 card c = {13, 'h'}; /* the king of hearts */ complex a[3][3] = { {{1.0, -0.1}, {2.0, 0.2}, {3.0, 0.3}}, {{4.0, -0.4}, {5.0, 0.5}, {6.0, 0.6}}, }; /* a[2][] is assigned zeroes */ struct fruit frt = {"plum", 150}; struct home_address { char *street; char *city_and_state; long zip_code; } address = {"87 West Street", "Aspen, Colorado", 80526}; struct home_address previous_address = {0};
36 구조체예제 /* 세명의데이터중에서학점이가장높은학생의학번, 이름, 학점을출력 */ #include <stdio.h> struct student{ int num; char name[20]; double grade; }; int main() { struct student s1={315, " 홍길동 ", 2.4}, s2={247, " 이순신 ", 3.7}, s3={330, " 세종대왕 ", 4.4}; struct student max; } max=s1; if(s2.grade > max.grade) max=s2; if(s3.grade > max.grade) max=s3; printf(" 학번 : %d\n", max.num); printf(" 이름 : %s\n", max.name); printf(" 학점 : %.1lf\n", max.grade); return 0; // 학생데이터에대한구조체선언 // 학번을저장할멤버 // 이름을저장할멤버 // 학점을저장할멤버 // 구조체변수의선언과초기화 // 학점이가장높은학생의데이터를저장할구조체변수 // 처음에홍길동의학점이가장높다고가정한다. // 각학생의학점을비교하여학점이가장높은 // 학생의데이터가 max 에저장되도록한다. // 학점이가장높은학생의각데이터를출력한다.
37 구조체사용 배열은대입연산이불가능하다. int ary1[5]={10,20,30,40,50}; int ary2[5]; ary2 = ary1; // 불가능하다. 각배열요소를일일이대입해야한다! 구조체변수는대입연산으로모든멤버들을복사할수있다. struct student s1={315, 홍길동, 2.4}; struct student max; max=s1;
38 구조체사용 구조체는대입연산이가능하므로함수의전달인자로줄수있다. - 최고학점의학생데이터를함수로출력해보자. 함수의호출 max_prn(max); // 구조체변수를전달인자로주고호출한다. 함수의정의 void max_prn(struct student max) { printf( 학번 : %d\n, max.num); printf( 이름 : %d\n, max.name); printf( 학점 : %d\n, max.grade); } // 매개변수는구조체변수를선언한다.
39 구조체사용 구조체를사용하면포인터없이도두변수의값을바꿀수있다. - 로보트의양쪽시력을바꾸는프로그램예 #include <stdio.h> struct vision{ double left; double right; }; struct vision exchange(struct vision); int main() { struct vision robot; } printf(" 로보트의시력을입력하세요 ( 좌, 우 ) : "); scanf("%lf%lf", &robot.left, &robot.right); robot=exchange(robot); printf(" 바뀐로보트의시력 ( 좌, 우 ) : %.1lf, %.1lf\n", robot.left, robot.right); return 0; struct vision exchange(struct vision robot) { double temp; } temp=robot.left; robot.left=robot.right; robot.right=temp; return robot;
40 구조체배열 구조체변수가많이필요하면배열로선언하여사용 - 주소록을만드는프로그램의예 (5 명의주소를저장할경우 ) struct address{ char name[20]; int age; char tel[20]; char addr[80]; }; // 이름을저장할멤버 // 나이를저장할멤버 // 전화번호를저장할멤버 // 주소를저장할멤버
41 구조체배열 구조체배열의참조된배열요소는구조체변수이므로실제데이터 를저장하기위해서는다시멤버를참조해야함 - list 배열의네번째배열요소의 age 멤버를참조할때 - list 배열의네번째배열요소의모든멤버에값을저장 strcpy(list[3].name, 홍길동 ); list[3].age=23; strcpy(list[3].tel, 012-345-6789 ); strcpy(list[3].addr, 울릉도동남쪽외로운섬독도 );
42 구조체배열 배열의초기화방법을그대로적용 단, 배열의요소가구조체이므로각각의초기값은구조체초기화 형식을사용 - list 배열의초기화 struct address list[5] = { { 홍길동, 23, 012-345-6789, 울릉도독도 }, { 이순신, 35, 111-222-3333, 서울건천동 }, { 장보고, 19, 222-333-4444, 완도청해진 }, { 유관순, 15, 333-444-5555, 충남천안 }, { 안중근, 45, 444-555-6666, 황해도해주 } };
43 구조체배열예제 /* 배열요소의값을반복문으로출력 */ #include <stdio.h> struct address { char name[20]; int age; char tel[20]; char addr[80]; }; int main() { struct address list[5]={{" 홍길동 ", 23, "012-345-6789", " 울릉도독도 "}, {" 이순신 ", 35, "111-222-3333", " 서울건천동 "}, {" 장보고 ", 19, "222-333-4444", " 완도청해진 "}, {" 유관순 ", 15, "333-444-5555", " 충남천안 "}, {" 안중근 ", 45, "444-555-6666", " 황해도해주 "}}; int i; } for(i=0; i<5; i++){ // 배열요소가 5개이므로 5번반복 printf("%10s%5d%15s%20s\n", list[i].name, list[i].age, list[i].tel, list[i].addr); } return 0;
44 공용체 union 공용체는구조체와비슷한구문형식을가지지만각멤버들은같은기억장소를공유함 공용체형은메모리의같은위치에저장될여러값 의집합을정의 저장된값을올바르게해석하는것은프로그래머 의책임
45 공용체변수선언 공용체변수선언 union int_or_float a, b, c; 이선언으로식별자 a, b, c에대한기억장소가할당 컴파일러는공용체의멤버중에서가장큰기억장소를요구하는멤버의요구만큼기억장소를할당 공용체의멤버접근방법은구조체의멤버접근방법과동일 첫번째멤버만초기화가능함
공용체의특성 하나의메모리공간을둘이상의변수가공유하는형태
47 형식 union 태그이름 { 자료형멤버 ; }; union data { char ch; int num; double y; };
48 #include <stdio.h> union data { /* 공용체정의 */ char ch; int num; double y; }; int main() { union data value; /* 공용체변수선언 */ value.ch = 'A'; printf("ch = %d\n", value.ch); value.num = 300; printf("num = %d\n", value.num); value.y = 45.234; printf("y = %.3f\n", value.y); } return 0;
49 공용체예제 공용체는모든멤버가하나의기억공간을공유하므로메모리를절약할수있지만다른멤버에의해서데이터가변질될위험 #include <stdio.h> union student{ int num; double grade; }; int main() { union student s1={315}; } printf(" 학번 : %d\n", s1.num); s1.grade=4.4; printf(" 학점 : %.1lf\n", s1.grade); printf(" 학번 : %d\n", s1.num); return 0; 출력결과 학번 : 315 학점 : 4.4 학번 : -1717986918 학번의초기값이학점멤버에의해서변질되었다.
열거형 열거형 (enumeration) 이란변수가가질수있는값들을미리열거해놓은자료형 ( 예 ) 요일을저장하고있는변수는 { 일요일, 월요일, 화요일, 수요일, 목요일, 금요일, 토요일 } 중의하나의값만가질수있다.
열거형의선언 enum days { SUN, MON, TUE, WED, THU, FRI, SAT }; 태그이름 값들을나열 열거형변수선언 enum days today; today = SUN; // OK!
열거형이필요한이유 다음과같이프로그램을작성할수있다. int today; today = 0; // 일요일 today = 1; // 월요일 되도록오류를줄이고가독성을높여야된다. 0 보다는 SUN 라는기호상수가더바람직하다. 의미를쉽게알수있기때문이다. today 에 9 와같은의미없는값이대입되지않도록미리차단하는것도필요하다.
열거형초기화 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,... 값을지정하지않으면 0 부터할당
열거형의예 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 입니다
열거형과다른방법과의비교 정수사용기호상수열거형 switch(code) { case 1: printf("lcd TV\n"); break; case 2: printf("pdp TV\n"); break; } 컴퓨터는알기쉬우나사람은기억하기어렵다. #define LCD 1 #define PDP 2 switch(code) { case LCD: printf("lcd TV\n"); break; case PDP: printf("pdp TV\n"); break; } 기호상수를작성할때오류를저지를수있다. enum tvtype { LCD, PDP }; enum tvtype code; switch(code) { case LCD: printf("lcd TV\n"); break; case PDP: printf("pdp TV\n"); break; } 컴파일러가중복이일어나지않도록체크한다.
typedef typedef 은새로운자료형 (type) 을정의 (define) C 의기본자료형을확장시키는역할 typedef old_type new_type; 새로운자료형을정의 typedef unsigned char BYTE; 기존의자료형 새로운자료형
58 typedef 를사용하여응용자료형을간단하게사용 Student s1; // 재정의된자료형으로간단하게구조체변수선언 형선언과동시에재정의하는방법도가능하다. typedef struct { int num; double grade; } Student; // 재정의될것이므로자료형의이름이필요없다. // 새로운자료형의이름을바로적어준다.
59 typedef 예제 #include <stdio.h> typedef struct { int num; double grade; } Student; void data_prn(student *); int main() { Student s1={315, 4.2}; } data_prn(&s1); return 0; // 구조체의선언과동시에자료형의재정의한다. // 함수의선언, 매개변수는 Student형의포인터변수 // Student형의변수선언과초기화 // Student 변수의포인터를전달한다. void data_prn(student *sp)// Student형을가리키는포인터변수 { printf(" 학번 : %d\n", sp->num); // 구조체포인터변수로멤버참조하기 printf(" 학점 : %.1lf\n", sp->grade); }
60
예제 #1 #include <math.h> struct point { int x; int y; }; int main(void) { struct point p1, p2; int xdiff, ydiff; double dist; printf(" 점의좌표를입력하시오 (x y): "); scanf("%d %d", &p1.x, &p1.y); printf(" 점의좌표를입력하시오 (x y): "); scanf("%d %d", &p2.x, &p2.y); xdiff = p1.x - p2.x; ydiff = p1.y - p2.y; dist = sqrt(xdiff * xdiff + ydiff * ydiff); 점의좌표를입력하시오 (x y): 10 10 점의좌표를입력하시오 (x y): 20 20 두점사이의거리는 14.142136 입니다. p1 (x,y) p2 (x,y) } printf(" 두점사이의거리는 %f 입니다.\n", dist); return 0;
예제 #2 구조체배열 #define SIZE 3 struct student { int number; char name[20]; double grade; }; int main(void) { struct student list[size]; int i; for(i = 0; i < SIZE; i++) { printf(" 학번을입력하시오 : "); scanf("%d", &list[i].number); printf(" 이름을입력하시오 : "); scanf("%s", list[i].name); printf(" 학점을입력하시오 ( 실수 ): "); scanf("%lf", &list[i].grade); } 학번을입력하시오 : 20070001 이름을입력하시오 : 홍길동학점을입력하시오 ( 실수 ): 4.3 학번을입력하시오 : 20070002 이름을입력하시오 : 김유신학점을입력하시오 ( 실수 ): 3.92 학번을입력하시오 : 20070003 이름을입력하시오 : 이성계학점을입력하시오 ( 실수 ): 2.87 학번 : 20070001, 이름 : 홍길동, 학점 : 4.300000 학번 : 20070002, 이름 : 김유신, 학점 : 3.920000 학번 : 20070003, 이름 : 이성계, 학점 : 2.870000 } for(i = 0; i< SIZE; i++) printf(" 학번 : %d, 이름 : %s, 학점 : %f\n", list[i].number, list[i].name, list[i].grade); return 0;
예제 #2 구조체배열 #include <stdio.h> struct vector { float x; float y; }; struct vector get_vector_sum(struct vector a, struct vector b); int main(void) { struct vector a = { 2.0, 3.0 }; struct vector b = { 5.0, 6.0 }; struct vector sum; } sum = get_vector_sum(a, b); printf(" 벡터의합은 (%f, %f) 입니다.\n", sum.x, sum.y); return 0; a + b a b
예제 #3 구조체와배열그리고포인터 (1) struct person { char name[20]; char phone[20]; }; int main() { struct person man={"thomas", "354-00xx"}; struct person * pman; pman=&man; // 구조체변수를이용한출력. printf("name : %s\n", man.name); printf("phone : %s\n", man.phone); // 구조체포인터를이용한출력 1. printf("name : %s\n", (*pman).name); printf("phone : %s\n", (*pman).phone); } // 구조체포인터를이용한출력 2. printf("name : %s\n", pman->name); printf("phone : %s\n", pman->phone); return 0;
예제 #3 구조체와배열그리고포인터 (2) #include <stdio.h> struct perinfo { char addr[30]; char tel[20]; }; struct person { char name[20]; char pid[20]; struct perinfo* info; }; int main() { struct perinfo info={"korea Seoul", "333-4444"}; struct person man={"mr. Lee", "820204-xxxx512"}; man.info=&info; } printf("name : %s\n", man.name); printf("pid : %s\n", man.pid); printf("addr : %s\n", man.info->addr); printf("tel : %s\n", man.info->tel); return 0;
예제 #4 Typedef 활용 #include <stdio.h> typedef struct point { int x; int y; } POINT; POINT translate(point p, POINT delta); int main(void) { POINT p = { 2, 3 }; POINT delta = { 10, 10 }; POINT result; result = translate(p, delta); printf(" 새로운점의좌표는 (%d, %d) 입니다.\n", result.x, result.y); } return 0;
참고문헌 열혈 C 프로그래밍, 윤성우, 오렌지미디어 쉽게풀어쓴 C 언어 Express, 천인국, 생능출판사 뇌를자극하는 C 프로그래밍, 서현우, 한빛미디어 쾌도난마 C 프로그래밍, 강성수, 북스홀릭 67