IT Japan

제 7장 - 구조체와 공용체 본문

IT/Programming

제 7장 - 구조체와 공용체

swhwang 2016. 3. 22. 00:14
반응형

지난 장까지 배운 자료의 구조는 모두 한 개의 데이터로 구성되는 것이었 

다. 이번 장에서는 복수개의 데이터형이 모여 하나의 자료를 구성하는 구 

조체와 하나의 데이터를 여러 변수가 함께 사용하는 공용체에 대하여 설명 

하겠다. 



1. 구 조 체 


구조체는 한 개의 자료가 여러 개의 데이터형으로 구성되는 복합 데이터 

형이다. 예를 들어, 어느 회사에서 인사 화일을 만든다고 가정할 때 인사 

화일들은 아래와 같은 구조를 가질 수 있다. 


name addr birth tel 

+-----+-----+--------+--------+ 

|이 름|주 소|생년월일|전화번호| ===> 인사 화일의 각 자료 

+-----+-----+--------+--------+ 


이와 같이 여러 개의 단순 항목(field)이 모여서 하나의 집단 항목(recor 

d)을 이룬 것을 구조체라고 한다. 그리고 레코드 자체에 하나의 이름(변수 

명)을 부여할 수 있는데, 이러한 구조체의 이름을 구조체 택(tag)이라고 

한다. 구조체의 선언은 다음과 같은 형식으로 한다. 


<형 식> struct 구조체 이름 { 

멤버 1의 선언문; 

멤버 2의 선언문; 

...........; 

멤버 n의 선언문; 

}; 


위의 자료를 이용하여 다음과 같이 구조체를 정의할 수 있다. 


struct insa { 

char name[8]; 

char addr[25]; 

long birth; 

char tel[8]; 

}; 


이와 같은 선언은 일반적으로 main()함수 앞에서 하는데 이는 구조체의 

구조 및 형식을 함수에서 인용할 수 있게 하기 위한 것이다. 구조체를 선 

언하면 구조체의 형식만 정의되고 실제 자료는 할당되지 않으므로 구조체 

형식의 선언 후에 구조체 변수를 따로 잡아 할당해야 한다. 


main() 

struct insa x,y,z; /* 변수 x,y,z를 insa의 구조를 갖는 */ 

/* 구조체로 선언 */ 

x.name="Park"; 

x.addr="Seoul"; 

x.birth=560604; 

x.tel="123-1234"; 

y.name="Kin"; 

..... 

z.name="Son"; 

..... 


구조체의 항목(멤버)을 참조하거나 구조체 요소에 값을 할당할 때는 구조 

체 항목 연산자(.)를 이용한다. 구조체는 개개의 항목(field)이 모여 집단 

항목을 이루는 복합 데이터형이나 화일을 관리하는 프로그램 등에서 아주 

요긴하게 쓰인다. 위의 구조체 변수에서 다음의 경우를 생각해 보자. 


&x : 구조체 변수 x의 시작 주소 

x.name[2] : x의 멤버 name의 3번째 문자를 가리킨다. 


구조체 변수는 static으로 선언된 경우나 외부 변수로 선언된 경우에 한 

하여 변수 전체의 초기화가 가능하다. static으로 선언된 경우에는 다음과 

같이 기술한다. 이때 멤버와 멤버 사이, 변수와 변수 사이에는 ,로 구분하 

며, 맨 마지막에 ; 를 찍는다. 


main() 

static struct insa x={"Park","Seoul",560604,"123-1234"}, 

y={"Kim","Pusan", ... }, 

z={"Son", ..... }; 


반면 구조체를 외부 변수로 선언할 경우에는 구조체의 선언과 동시에 구 

조체 변수를 설정하는 것은 물론, 각각의 멤버에 초기치를 부여할 수도 있 

다. 


struct insa { ( , 와 ; 가 쓰인 곳을 확인하기 바란다) 

char name[8]; 

char addr[25]; | struct insa { 

lond birth; | char *name; 

char tel[8]; | char *addr; 

} x={"Park", ....... }, | long birth; 

y={ ........... }, | char *tel; 

z={ ........... }; | }; 


구조체의 멤버는 포인터형으로 선언할 수 있는데 우리가 다루고 있는 구 

조체를 포인터형으로 바꾸면 위의 우측과 같다. 



2. 구조체 배열 


구조체의 경우에도 다음과 같이 배열을 사용할 수 있다. 


struct insa kcom[10]; 


이 경우에는 요소가 10인 구조체 배열 kcom이 준비되며, kcom에는 구조체 

배열의 시작 주소가, 메모리 내부에는 각 멤버가 저장될 공간이 확보된다. 

이 구조체는 아래 그림과 같이 기억장소가 할당된다. 


name addr birth tel 

+----------+-----------+---------+----------+ 

|9문자 배열|25문자 배열|long 정수|8문자 배열| 

+----------+-----------+---------+----------+ 

kcom[0]| | | | | 

+----------+-----------+---------+----------+ 

...... 

+----------+-----------+---------+----------+ 

kcom[9]| | | | | 

+----------+-----------+---------+----------+ 


그리고 구조체 배열에서 각 멤버를 참조할 때는 다음과 같이 기술한다. 


buf=kcom[3].addr 


즉, 배열 첨자를 먼저 기술한 후, 구조체 항목 연산자(.)를 찍고 항목명 

을 기술한다. 구조체 배열의 선언 방법과 사용에 따른 제반 절차는 일반배 

열의 경우와 동일하다. 그러면, 예제를 보자. 


<예제1> 다음 자료를 구조체 배열에 기억시킨 후 화면에 출력하는 프로그 

램 작성. 

================================================== 

번호 성 명 주 소 생일 전화번호 

================================================== 

1 김 유신 서울 성동구 560604 123-5432 

2 강 감찬 부산 동래구 600430 43-4455 

3 이 순신 수원 화서동 661122 2-1234 

4 고 바우 성남 상대원동 610530 212-1155 

================================================== 


<리스트1> 

#include 

struct insa { /* 구조체의 형식 선언 */ 

char *name; /* 화일의 전구간서 사용가능 */ 

char *addr; 

long birth; 

char *tel; 

}; 


main() 

struct insa in[]={ /* 구조체 배열의 초기화 */ 

{"김 유신","서울 성동구",560604,"123-5432"}, 

{"강 감찬","부산 동래구",600430,"43-4455"}, 

{"이 순신","수원 화서동",661122,"2-1234"}, 

{"고 바우","성남 상대원동",610530,"212-1155"} 

}; 

int i; 

printf("==================================================\n"); 

printf("번호 성 명 주 소 생일 전화번호\n"); 

printf("==================================================\n"); 

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

printf("%2d %-10s%-17s%6ld%10s\n",i+1,in[i].name, 

in[i].addr,in[i].birth,in[i].tel); 

printf("==================================================\n"); 



3. 중첩된 구조체(nested structure) 


종종 한 구조체가 또 다른 구조체를 포함시키는 경우가 있다. 아래의 그 

림을 보자. 

fellow (구조체명 guy) 

+------------------------+-----------+-----+-------+ 

| handle(구조체명 names) | | | | 

+------------+-----------+ favfood | job | income| 

| first[LEN] | last[LEN] |즐기는 음식|직 업| 수 입| 

+------------+-----------+-----------+-----+-------+ 

| | | | | | 


위의 그림은 구조체 guy중에 구조체 name이 들어있는 경우이다. 


<예제2> 중첩된 구조체의 예 (철모르는 어린이의 편지) 


<리스트2> 

#define LEN 20 


struct names { 

char first[LEN]; /* 성명 구조체의 이름 항목 */ 

char last[LEN]; /* 성명 구조체의 성 항목 */ 

}; 

struct guy { 

struct names handle; /* 구조체 guy의 내부에 다시 구조체 */ 

/* names를 가지는 구조체 변수 handle선언 */ 

char favfood[LEN]; 

char job[LEN]; 

float income; 


}; 


main() 

static struct guy fellow={ /*구조체guy의 구조를 가지는 변수 */ 

/* fellow의 초기화 */ 

{"태지","서"}, /* 구조체names의 초기화이므로 { }로 싼다 */ 

"돼지 족발", 

"락 가수", 

1000000 

}; 


printf("안녕하세요? %s아저씨,\n",fellow.handle.first); 

printf("새로나온 노래 정말 괜찮테요?\n\n"); 

printf("저는 %s%s아저씨가 %s을 좋아한다는 소릴 듣고\n", 

fellow.handle.last, fellow.handle.first, fellow.favfood); 

printf("너무너무 야만인이라고 생각했거든요? \n"); 

printf("하지만 아저씨가 이번에 수해돕기 성금으로\n"); 

printf("%.0f원이나 내셨다는 소리를 듣고",fellow.income); 

printf("정말 기분 좋았어요.!\n"); 

printf("앞으로 열심히 하셔서 멋진 %s가 되세요!!\n\n\n", 

fellow.job); 

getch(); 



4. 구조체 포인터 변수 


구조체에 대해서도 포인터를 사용할 수가 있다. 구조체 포인터 변수가 타 

당한 이유를 들면, 


1) 구조체에 대한 포인터가 구조체 자체 보다는 처리하기가 용이하다. 

2) 구조체는 함수의 인자로서 전달될 수 없지만 구조체에 대한 포인터 

는 가능하다. 

3) 대다수의 고도의 데이터 표현은 또 다른 구조체에 대한 포인터를 구 

성원으로 하는 구조체에 의해 가능하다. 


struct insa *p;처럼 p를 구조체 insa를 가리키는 포인터로 선언할 수 있 

다. 그러므로 ++p라는 표현을 사용하면 구조체 포인터를 1만큼 증가시켜 i 

nsa의 크기 만큼이 실제로 증가된다. 또한 p+n번째 구조체를 참조하는 경 

우에는 p+n으로 표현한다. 

구조체 포인터 변수에서 구조체의 멤버를 참조할 경우에는 구조체 연산자 

'->'를 사용하여 다음과 같이 기술한다. 


p -> addr : p가 가리키는 구조체의 항목 addr을 나타낸다. 

(p+n) -> name : p+n번째 구조체의 name 항목을 나타낸다. 


구조체 연산자 . 와 -> 는 우선순위가 가장 높다. 그러므로 아래 두 식의 

결과는 서로 다르게 작용한다. 


++p -> addr (++p) -> addr 


전자의 경우에는 ++(p->addr)로 해석되어 p의 멤버 addr의 값을 하나 증 

가시킨다. 그리고 후자는 다음번 구조체의 addr항목을 가리키게 된다. 


함수의 인수를 이용하여 구조체를 함수로 전달할 경우에는 구조체 포인터 

를 사용한다. 이 때 주는 측은 구조체의 주소를, 받는 측은 구조체의 포인 

터를 이용하여 주고 받게 된다. 이렇게 함수에서 구조체의 입력을 받고, 

다른 함수에서는 구조체 포인터에 의해 인수를 전달하는 과정을 예제로 직 

접 확인해 보자. 


<예제3> <예제1>에서 설계한 구조체에 사원의 자료를 함수에서 입력받아 

화면에 출력하는 프로그램 작성. 


<리스트3> 

#include 

#include 

struct insa { /* 구조체의 형식 선언 */ 

char name[12]; /* 화일의 전구간서 사용가능 */ 

char addr[15]; 

long birth; 

char tel[9]; 

}; 

int input(); /* 사용자 함수의 선언 */ 

void display(); 


main() 

struct insa arr[10]; /* 옆의 두 라인을, */ 

struct insa *p; /* struct insa *p=arr;로도 가능 */ 

int i; 

p=arr; 


for (i=0;i <= 10;i++,p++) 

if (input(p) == 0) break; /* input()함수에서 0을 돌려주면 */ 

display(arr,i); /* 루프 탈출 */ 

} /* 구조체 '배열명'arr은 그 구조체의 첫 주소를 나타내므로 */ 

/* display(p,i)대신 display(arr,i)도 가능한 표현임 */ 

/* 사실 p는 p++로 증가되어 버렸으므로 구조체의 첫주소를 가리 */ 

/* 키지 않는다. */ 


int input(ptr) /* 이 두라인을, */ 

struct insa *ptr; /* int input(struct insa *ptr)로도 가능 */ 

char buff[20]; /* 임시 문자열 저장 영역 확보 */ 

printf("성명:"); 

gets(ptr->name); 

if (ptr->name[0] == '\0') return(0); /* 이름을 입력않고, 그냥*/ 

printf("주소:"); /* 엔터치면 0을 되돌리고 함수를 종료한다. */ 

gets(ptr->addr); 

printf("생년월일(yymmdd):"); 

gets(buff); /* 생일을 문자열로 입력받은 후, */ 

ptr->birth=atol(buff); /* 배정도 정수로 변환시킨다 */ 

printf("전화번호:"); 

gets(ptr->tel); 

printf("\n"); 

return(1); /* 정상적으로 입력되면 1을 되돌린다. */ 


void display(s,k) /* 이 세행을 한줄로 표현하면, */ 

struct insa *s; /* */ 

int k; /* void display(struct insa *s,int k) */ 

int i; 

printf("==================================================\n"); 

printf("번호 성 명 주 소 생일 전화번호\n"); 

printf("==================================================\n"); 

for (i=0;i <= k;i++) { 

printf("%2d %-10s%-17s%6ld%10s\n",i+1, s->name, 

s->addr, s->birth, s->tel); 

s++; 

printf("==================================================\n"); 



5. 공 용 체 (Union) 


공용체는 하나의 데이터를 여러 개의 변수가 공용할 수 있게 해주는 데이 

터 형이다. 공용체의 정의 방법과 선언 방법은 구조체와 동일하다. 단, 구 

조체는 모든 자료가 별개의 기억장소를 배정받지만 공용체의 경우에는 가 

장 긴 것의 자료 형에 맞추어 메모리를 할당받는다. 공용체의 멤버로는 주 

로 구조체를 사용하며, 공용체의 정의 형식은 다음과 같다. 


union 공용체 택 { 멤버1; 멤버2; ...; }; 


<예제4> 공용체의 사용 보기 


<리스트4> | <리스트4>의 공용체는 아래와 같 

union abc { | 은 형태로 기억장소를 배정한다 

char a; | 

int b; | | | 

long c; | +------+ ----+ ---+ 

}; | | 78 | - a | | 

main() | +------+ | b | 

{ | | 56 | | | 

union abc val; | +------+ ----+ | c 

val.c=(long)0x12345678; | | 34 | | 

printf("c=%08lx\n", val.c); | +------+ | 

printf("b=%08x\n", val.b); | | 12 | | 

printf("a=%08x\n",val.a); | +------+ ---+ 

} | | | 


위의 그림과 같이 상위 바이트와 하위 바이트가 바뀌어 저장되는 것을 역 

워드 형식이라고 하는데 80x계의 CPU에서는 역워드 형식으로 자료가 저장 

된다. <리스트4>내의 프로그램에서는 각각의 바이트를 독립적으로 억세스 

할 수 없지만 공용체의 멤버로 구조체를 사용하면 각각의 바이트를 개별적 

으로 억세스할 수 있다. 


<예제5> 공용체의 사용 보기 (II) 


<리스트5> 

struct str { | <리스트5>의 공용체는 아래와 

char c1; | 같은 형태로 기억장소를 배정 

char c2; | 한다. 

char c3; | 

char c4; | | | 

}; | +------+ --+ -----+ --+ 

union abc { | | 78 | c1| - a | | 

struct str chr; | +------+ | |b | 

char a; | | 56 | c2| | | 

int b; | +------+ | -----+ |c 

long c; | | 34 | c3|구조체 | 

}; | +------+ | chr | 

main() | | 12 | c4| | 

{ | +------+ --+ ---------+ 

union abc val; | | | 

val.c=(long)0x12345678; | 

printf("a=%8lx\n", val.a); | 

printf("b=%8x\n", val.b); | <리스트5>에서 c3를 가리킬 경 

printf("c=%8x\n", val.c); |우에는 val.chr.c3로 기술해야 

printf("c1=%8x\n",val.chr.c1); |한다. 이것은 '공용체 val에 속 

printf("c2=%8x\n",val.chr.c2); |해있는 구조체 chr의 멤버 c3'라 

printf("c3=%8x\n",val.chr.c3); |는 의미이다. 구조체의 요소를 

printf("c4=%8x\n",val.chr.c4); |공용체로 선언해 두면 자료를 전 

} |체적으로 통합하여 다룰 수 있고 

개별적으로도 다룰 수 있다. 


구조체나 공용체를 함수의 인수로 사용할 때는 구조체나 공용체의 주소를 

건내주어야 하며, 받는 측에서는 그것을 포인터로 받아온다. 예를 들어 te 

st()라는 함수에 <리스트5>에서 정의한 공용체 val을 전달하려면 다음과 

같이 하면 된다. 


호출측 : test(&val); 

... 

피호출측 : void test(union val *abc) 

{ .... 


test()함수에서는 공용체 val을 abc라는 이름으로 사용한다. 구조체와 공 

용체의 활용은 초보자에게는 다소 어려운 개념이므로 보다 깊은 내용은 생 

략하겠다. 

 


반응형

'IT > Programming' 카테고리의 다른 글

[Python]Python으로 OpenCV를 사용  (0) 2017.06.07
제 8장 - 화일 조작 / 기타의 것들  (0) 2016.03.22
제 6장 - 배열과 포인터  (0) 2016.03.22
제 5장 - 함수 / 기억부류  (0) 2016.03.22
제 4장 - 연 산 자  (0) 2016.03.22
Comments