Transcript 11장 포인터
쉽게 풀어쓴 C언어 Express
제13장 구조체
© 2012 생능출판사 All rights reserved
이번 장에서 학습할 내용
•
•
•
•
•
구조체
구조체와 포인터
공용체
열거형
typedef
© 2012 생능출판사 All rights reserved
구조체는 서로 다른
데이터들을 하나로
묶는 중요한
도구입니다.
자료형의 분류
기본 자료형: char, int, float, double, …
자료형
파생 자료형: 배열, 구조체, 공용체, …
© 2012 생능출판사 All rights reserved
구조체의 필요성
학생 데이터를 하나로 묶어서 처리하려면?
int number;
char name[10];
double grade;
© 2012 생능출판사 All rights reserved
구조체를 사
용하면 변수
들을 하나로
묶을 수 있
습니다.
구조체와 배열
구조체(structure): 레코드(record)
같은 타입의 집합
© 2012 생능출판사 All rights reserved
다른 타입의 집합
구조체 선언
태그(tag): 이름
구조체 선언 형식
struct 태그 {
자료형 멤버1;
자료형 멤버2;
...
};
struct student {
int
number;
char
name[10];
double grade;
};
멤버(필드)
© 2012 생능출판사 All rights reserved
구조체 선언의 예
// x, y값으로 이루어지는 화면의 좌표
struct point {
int x;
// x 좌표
int y;
// y 좌표
};
// 직사각형
struct rect {
int x, y;
int width, height;
};
// 복소수
struct complex {
double real;
double imag;
};
// 직원
struct employee {
char name[20];
int age;
int sex;
int salary;
};
// 실수부
// 허수부
// 날짜
struct date {
int month;
int day;
int year;
};
© 2012 생능출판사 All rights reserved
//
//
//
//
이름
나이
성별
월급
구조체 변수 선언
구조체 정의(선언)와 구조체 변수 선언은 다름
구조체 정의: 사용자 정의 자료형 선언
© 2012 생능출판사 All rights reserved
구조체의 초기화
중괄호를 이용하여 초깃값 나열
struct student {
int number;
char name[10];
double grade;
};
struct student s1 = { 24, "Kim", 4.3 };
© 2012 생능출판사 All rights reserved
구조체 멤버 참조
구조체 멤버 참조 연산자:
struct student {
int number;
char name[10];
double grade;
};
struct student s1;
s1.number = 24;
strcpy(s1.name, "Kim");
s1.grade = 4.3;
© 2012 생능출판사 All rights reserved
.
예제
#include <stdio.h>
#include <string.h>
struct student {
int number;
char name[10];
double grade;
};
int main(void) {
struct student s;
s.number = 20070001;
strcpy(s.name, "홍길동");
s.grade = 4.3;
printf("학번: %d\n", s.number);
printf("이름: %s\n", s.name);
printf(“학점: %f\n", s.grade);
}
return 0;
© 2012 생능출판사 All rights reserved
학번: 20070001
이름: 홍길동
학점: 4.300000
예제
#include <stdio.h>
struct student {
int number;
char name[10];
double grade;
};
int main(void) {
struct student s;
printf("학번을 입력하시오: ");
scanf("%d", &s.number);
printf("이름을 입력하시오: ");
scanf("%s", s.name);
printf("학점을 입력하시오(실수): ");
scanf("%lf", &s.grade);
}
printf("학번: %d\n", s.number);
printf("이름: %s\n", s.name);
printf("학점: %f\n", s.grade);
return 0;
© 2012 생능출판사 All rights reserved
학번을 입력하시오: 20070001
이름을 입력하시오: 홍길동
학점을 입력하시오(실수): 4.3
학번: 20070001
이름: 홍길동
학점: 4.300000
예제
#include <stdio.h>
#include <math.h>
struct point { int x, y; };
점의 좌표를 입력하시오(x y): 10 10
점의 좌표를 입력하시오(x y): 20 20
두 점 사이의 거리는 14.142136입니다.
int main(void) {
struct point p1, p2;
int xdiff, ydiff;
double dist;
p2 (x,y)
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);
}
printf("두 점 사이의 거리는 %f입니다.\n", dist);
return 0;
© 2012 생능출판사 All rights reserved
p1 (x,y)
구조체 중첩
struct date {
int month;
int day;
int year;
};
struct student {
int number;
char name[10];
struct date dob;
double grade;
};
struct student s1;
s1.dob.month = 3;
s1.dob.day = 29;
s1.dob.year = 1983;
© 2012 생능출판사 All rights reserved
s1 number
name
dob month
day
year
// 구조체 안에 구조체 포함
grade
예제
#include <stdio.h>
struct point { int x, y; };
struct rect { struct point p1, p2; };
왼쪽 상단의 좌표를 입력하시오: 1 1
오른쪽 하단의 좌표를 입력하시오: 6 6
면적은 25이고 둘레는 20입니다.
int main(void) {
struct rect r;
int w, h, area, peri;
p1(x,y)
printf("왼쪽 상단의 좌표를 입력하시오: ");
scanf("%d %d", &r.p1.x, &r.p1.y);
printf("오른쪽 하단의 좌표를 입력하시오: ");
scanf("%d %d", &r.p2.x, &r.p2.y);
p2(x,y)
y
w = r.p2.x - r.p1.x; h = r.p2.y - r.p1.y;
area = w * h; peri = 2 * (w + h);
printf("면적은 %d이고 둘레는 %d입니다.\n", area, peri);
}
return 0;
© 2012 생능출판사 All rights reserved
x
구조체 변수의 대입과 비교
같은 타입의 구조체 변수끼리 대입은 가능하지만 비교는 불가능
struct point { int x, y; };
int main(void) {
struct point p1 = { 10, 20 };
struct point p2 = { 30, 40 };
p2 = p1;
// 대입 가능
if (p1 == p2) // 비교 불가능 컴파일 오류!!
printf("p1과 p2가 같습니다.");
if (p1.x == p2.x && p1.y == p2.y) // 올바른 비교
printf("p1과 p2가 같습니다.");
}
return 0;
© 2012 생능출판사 All rights reserved
구조체 배열
구조체 배열의 선언
struct student {
int number;
char name[10];
double grade;
};
struct student list[3];
list[1].number = 24;
strcpy(list[1].name, "Kim");
list[1].grade = 4.3;
구조체 배열의 초기화
struct student list[3] = {
{ 25, "Mun", 3.92 },
{ 24, "Kim", 4.3 },
{ 23, "Tim", 2.9 }
};
© 2012 생능출판사 All rights reserved
예제
#include <stdio.h>
학번을 입력하시오: 20070001
#define SIZE 3
이름을 입력하시오: 홍길동
학점을 입력하시오(실수): 4.3
struct student {
학번을 입력하시오: 20070002
int number;
이름을 입력하시오: 김유신
char name[20];
학점을 입력하시오(실수): 3.92
double grade;
학번을 입력하시오: 20070003
};
이름을 입력하시오: 이성계
int main(void) {
학점을 입력하시오(실수): 2.87
struct student list[SIZE];
학번: 20070001, 이름: 홍길동, 학점: 4.300000
학번: 20070002, 이름: 김유신, 학점: 3.920000
int i;
학번: 20070003, 이름: 이성계, 학점: 2.870000
for (i = 0; i < SIZE; i++) {
printf("학번을 입력하시오: ");
scanf("%d", &list[i].number);
printf("이름을 입력하시오: ");
scanf("%s", list[i].name);
printf("학점을 입력하시오(실수): ");
scanf("%lf", &list[i].grade);
}
for (i = 0; i < SIZE; i++)
printf("학번: %d, 이름: %s, 학점: %f\n", list[i].number, list[i].name, list[i].grade);
return 0;
}
© 2012 생능출판사 All rights reserved
구조체를 가리키는 포인터
구조체를 가리키는 포인터
struct student {
int number;
char name[10];
double grade;
};
struct student s = { 20070001, "홍길동", 4.3 };
struct student *p = &s;
printf("학번=%d ,이름=%s, 학점=%f \n", s.number, s.name, s.grade);
printf("학번=%d, 이름=%s, 학점=%f \n", (*p).number, (*p).name, (*p).grade);
int n = 3;
p ptr
struct s {
…
int *ptr;
…
} p;
p.ptr = &n;
*p.ptr = 4;
// *(p.ptr) = 4; n = 4;
© 2012 생능출판사 All rights reserved
n
34
-> 연산자
-> : 포인터로 구조체 멤버를 참조할 때 사용
(*p).member p->member
#include <stdio.h>
struct student {
int number;
char name[20];
double grade;
};
학번=20070001, 이름=홍길동, 학점=4.300000
학번=20070001, 이름=홍길동, 학점=4.300000
학번=20070001, 이름=홍길동, 학점=4.300000
int main(void) {
struct student s = { 20070001, "홍길동", 4.3 };
struct student *p = &s;
printf("학번=%d, 이름=%s, 학점=%f \n", s.number, s.name, s.grade);
printf("학번=%d, 이름=%s, 학점=%f \n", (*p).number, (*p).name, (*p).grade);
printf("학번=%d ,이름=%s, 학점=%f \n", p->number, p->name, p->grade);
}
return 0;
© 2012 생능출판사 All rights reserved
포인터 멤버를 가지는 구조체
#include <stdio.h>
struct date { int month, day, year; };
struct student {
int number;
char name[20];
double grade;
struct date *dob;
};
s
number
name
grade
dob
학번: 20070001
이름: Kim
학점: 4.300000
생년월일: 1980년 3월 20일
int main(void) {
struct date d = { 3, 20, 1980 };
struct student s = { 20070001, "Kim", 4.3, &d }; // s.dob = &d;
printf("학번: %d\n", s.number);
printf("이름: %s\n", s.name);
printf("학점: %f\n", s.grade);
printf("생년월일: %d년 %d월 %d일\n", s.dob->year, s.dob->month, s.dob->day);
}
return 0;
© 2012 생능출판사 All rights reserved
month
day
year
구조체와 함수
구조체를 함수의 인자로 전달하는 경우
복사본 전달
구조체가 크면 시간과 메모리 측면에서 비효율적
int f(struct student s) { … }
구조체의 포인터를 함수의 인자로 전달하는 경우
시간과 메모리 절약 가능
원본 훼손 가능
int f(struct student *p) { … }
읽기만 하고 변경하지 않는 경우: const 선언
int f(const struct student *p) { … }
포인터를 통한 구조체 변경 방지
© 2012 생능출판사 All rights reserved
구조체 인자와 구조체 반환
#include <stdio.h>
struct vector { float x, y; };
a+b
b
struct vector get_vector_sum(struct vector a, struct vector b);
a
int main(void) {
struct vector a = { 2.0F, 3.0F }, b = { 5.0F, 6.0F }, sum;
sum = get_vector_sum(a, b);
printf("벡터의 합은 (%f, %f)입니다.\n", sum.x, sum.y);
return 0;
}
struct vector get_vector_sum(struct vector a, struct vector b) {
struct vector result;
result.x = a.x + b.x;
벡터의 합은 (7.000000, 9.000000)입니다.
result.y = a.y + b.y;
return result;
}
© 2012 생능출판사 All rights reserved
구조체 포인터 인자
#include <stdio.h>
struct vector { float x, y; };
void vector_input(struct vector *p);
void vector_output(const struct vector *p);
int main(void) {
struct vector v;
vector_input(&v);
vector_output(&v);
return 0;
}
벡터 입력: 1.2 3.4
(1.2, 3.4)
© 2012 생능출판사 All rights reserved
void vector_input(struct vector *p) {
printf("벡터 입력: ");
scanf("%f %f", &p->x, &p->y);
}
void vector_output(const struct vector *p) {
printf("(%g, %g)", p->x, p->y);
}
구조체 포인터 반환
#include <stdio.h>
struct vector { float x, y; };
struct vector *vector_input(struct vector *p);
void vector_output(const struct vector *p);
int main(void) {
struct vector v;
vector_output(vector_input(&v));
return 0;
}
벡터 입력: 1.2 3.4
(1.2, 3.4)
© 2012 생능출판사 All rights reserved
struct vector *vector_input(struct vector *p) {
printf("벡터 입력: ");
scanf("%f %f", &p->x, &p->y);
return p;
}
void vector_output(const struct vector *p) {
printf("(%g, %g)", p->x, p->y);
}
인자 전달: 배열과 구조체
배열 인자: 포인터로 전달(원본 전달)
구조체: 복사본 전달
void f(int ary[ ]) { ++ary[0]; }
// void f(int *ary) { ++*(ary + 0); }
…
int a[3] = { 10, 20, 30 };
f(a);
// a[0]: 11
© 2012 생능출판사 All rights reserved
struct s_array { int a[3]; };
void f(struct s_array sa) { ++sa.a[0]; }
…
struct s_array s = { { 10, 20, 30 } };
f(s);
// s.a[0]: 10
공용체
공용체(union)
같은 메모리 영역을 멤버들이 공유
공용체 선언 및 사용 방법은 구조체와 유사
union example {
char c;
int i;
};
// 메모리 공유
// 메모리 공유
공용체는 첫째 멤버로만 초기화 가능
union u_tag { float v; char *s; };
union u_tag u = 3.4F;
// O
union u_tag u = "name";
// X
© 2012 생능출판사 All rights reserved
예제
#include <stdio.h>
union example {
char c;
int i;
};
v
c
i
int main(void) {
union example v;
v.c = 'A';
printf("v.c = %c\n", v.c);
v.i = 10000;
printf("v.i = %d\n", v.i);
}
return 0;
© 2012 생능출판사 All rights reserved
v.c = A
v.i = 10000
A
10000
IP 주소 예제
#include <stdio.h>
union ip_address {
unsigned char saddr[4];
unsigned int iaddr;
};
int main(void) {
union ip_address addr =
{ 0x7F, 0x00, 0x00, 0x01 };
printf("%08X\n", addr.iaddr);
return 0;
}
0100007F
(little endian)
7F000001
(big endian)
© 2012 생능출판사 All rights reserved
addr
saddr
7F
iaddr
00
00
01
01 00 00 7F
(little endian)
addr
saddr
iaddr
7F
00
00
7F 00 00 01
(big endian)
01
공용체 활용 예제
#include <stdio.h>
// variable types
#define INT
1
#define REAL 2
#define STR
3
struct s_var {
// variable structure
char *name;
int type;
union {
// value
int ival;
double rval;
char *sval;
} u;
};
void out_var(const struct s_var *p);
int main() {
struct s_var v;
v.name = "n"; v.type = INT;
v.u.ival = 1; out_var(&v);
v.name = "r"; v.type = REAL;
v.u.rval = 3.4; out_var(&v);
© 2012 생능출판사 All rights reserved
v.name = "s"; v.type = STR;
v.u.sval = "C Language"; out_var(&v);
}
return 0;
void out_var(const struct s_var *p) {
printf("%s: ", p->name);
switch (p->type) {
case INT:
printf("INT, %d\n", p->u.ival); break;
case REAL:
printf("REAL, %g\n", p->u.rval); break;
case STR:
printf("STR, %s\n", p->u.sval); break;
default:
printf("unknown type\n"); break;
}
}
n: INT, 1
r: REAL, 3.4
s: STR, C Language
구조체와 공용체의 크기
정렬 제한 조건(alignment constraint)
자료형
short(2B)
int(4B)
float(4B)
double(8B)
메모리 위치
2의 배수
4의 배수
4의 배수
4/8의 배수
// anonymous structure
struct { char c; int n; } s;
sizeof s : 8
c
n
struct { char a; char b; int n; } s;
sizeof s : 8
a b
n
© 2012 생능출판사 All rights reserved
union { char c; int n; } u;
sizeof u : 4
c
n
struct { char a; int n; char b; } s;
sizeof s : 12
a
n
b
열거형
열거형(enumeration type): 정수형 상수 심볼 정의
enum days { SUN, MON, TUE, WED, THU, FRI, SAT };
//
0,
1,
2,
3,
4,
5,
6
태그 이름
정수형 상수 심볼
열거형 변수
enum days today;
today = SUN;
printf("%d\n", MON);
© 2012 생능출판사 All rights reserved
// today = 0;
// 1
#define SUN 0
#define MON 1
…
#define SAT 6
열거형
기호 상수(symbolic constant) 정의
literal을 사용하는 것보다 가독성이 좋고 수정이 용이
#define보다 편리하고 오류 가능성이 적음
자동으로 연속된 값 설정 가능
블록 구조 적용
단점: 정수형 상수만 정의 가능
#define PI 3.14
#define UNIV "SKHU"
enum { PI = 3.14 };
enum { UNIV = "SKHU" };
© 2012 생능출판사 All rights reserved
//
//
//
//
O
O
X
X
#define INT 1
#define REAL 2
#define STR 3
// anonymous enum
enum { INT = 1, REAL, STR };
//
1,
2,
3
열거형
literal
#define
열거형
switch (code) {
case 1:
printf("LCD TV\n");
break;
case 2:
printf("PDP TV\n");
break;
}
#define LCD 1
#define PDP 2
기억하기 어렵고,
가독성이 나쁘며,
수정이 어려움
실수 가능성이 있고,
블록 구조가 적용되지 않음
실수 가능성이 적고,
블록 구조가 적용됨
#define LCD 1
#define PDP 1
정수형만 가능
© 2012 생능출판사 All rights reserved
switch (code) {
case LCD:
printf("LCD TV\n");
break;
case PDP:
printf("PDP TV\n");
break;
}
enum { LCD, PDP };
switch (code) {
case LCD:
printf("LCD TV\n");
break;
case PDP:
printf("PDP TV\n");
break;
}
열거형 초기화
enum { E0, E1 = 3, E2 };
// E0 = 0, E1 = 3, E2 = 4
enum { C1 = 'A', C2, C3 };
// C1 = 'A', C2 = 'B', C3 = 'C'
enum Colors { Red = 0xFF << 16, Green = 0xFF << 8, Blue = 0xFF };
enum Boolean { FALSE, TRUE };
enum levels { low = 1, medium, high };
enum CarTypes { sedan, suv, sports_car, van, pickup, convertible };
© 2012 생능출판사 All rights reserved
예제
#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 = WED;
printf("%d번째 요일은 %s입니다\n", d, days_name[d]);
}
return 0;
3번째 요일은 Wednesday입니다
© 2012 생능출판사 All rights reserved
typedef
typedef: 기존 자료형에 새로운 타입 이름 부여
typedef
old_type
new_type;
typedef unsiged char BYTE;
BYTE index;
// unsigned char index;
typedef int INT32;
typedef unsigned int UINT32;
INT32 i;
// int i;
UINT32 k;
// unsigned int k;
struct point { int x, y; };
typedef struct point POINT;
POINT a, b;
// struct point a, b;
© 2012 생능출판사 All rights reserved
typedef struct { int x, y; } POINT;
POINT a, b;
// struct { int x, y; } a, b;
예제
#include <stdio.h>
typedef struct { int x, y; } POINT;
새로운 점의 좌표는 (12, 13)입니다.
POINT move(POINT p, POINT delta);
int main(void) {
POINT p = { 2, 3 }, delta = { 10, 10 }, result;
result = move(p, delta);
printf("새로운 점의 좌표는 (%d, %d)입니다.\n", result.x, result.y);
}
return 0;
POINT move(POINT p, POINT delta) {
POINT new_p = { p.x + delta.x, p.y + delta.y };
return new_p;
}
© 2012 생능출판사 All rights reserved
typedef
이식성(portability) 향상
기계 독립적인 코드 작성 가능
typedef unsigned long size_t;
// int: 2B, long: 4B 인 경우
typedef long INT32; // 4B 정수형
// int: 4B, long: 8B 인 경우
typedef int INT32; // 4B 정수형
#define은 블록 구조가 적용되지 않고, 타입 정의 기능이 제한적임
#define INT32 int
INT32 n;
// int n;
#define String char *
String s1, s2; // char * s1, s2;
// s1: char *, s2: char
typedef char *String;
String s1, s2; // char *s1, *s2;
// s1: char *, s2: char *
문서화 기능
별도의 주석 없이도 자체적으로 설명력이 있음 가독성 향상
typedef int Height;
Height h1, h2;
© 2012 생능출판사 All rights reserved
typedef int Weight;
Weight w1, w2;
예제
#include <stdio.h>
typedef struct {
int number;
char name[20];
double grade;
} Student;
Student LIST[ ] = {
{ 20120001, "홍길동", 4.2 },
{ 20120002, "김철수", 3.2 },
{ 20120003, "김영희", 3.9 }
};
int main(void) {
Student *best = LIST;
Student *p = LIST + 1;
Student *end = LIST + (sizeof LIST / sizeof LIST[0]);
for ( ; p < end; p++)
if (p->grade > best->grade)
best = p;
printf("수석: %s, %d, %g\n",
best->name, best->number, best->grade);
}
return 0;
수석: 홍길동, 20120001, 4.2
© 2012 생능출판사 All rights reserved