구조체의 정의와 typedef 선언
typedef 선언
typedef 선언은 기존에 존재하는 자료형의 이름에 새 이름을 부여하는 것을 목적으로 하는 선언
// 예를 들어서 다음과 같이 typedef 선언을 하게 되면,
typedef int INT; // int의 또 다른 이름 INT를 부여!
/*
이로 인해 다음의 뜻을 컴파일러에게 전달
"자료형의 이름 int에 INT라는 이름을 추가로 붙여줍니다"
따라서 위의 선언 이후로는 다음의 형태로 int형 변수를 선언할 수 있다.
*/
INT num; // int num; 과 동일한 선언
INT * ptr; // int * ptr; 과 동일한 선언
#include <stdio.h>
typedef int INT;
typedef int * PTR_INT;
typedef unsigned int UINT;
typedef unsigned int * PTR_UINT;
typedef unsigned char UCHAR;
typedef unsigned char * PTR_UCHAR;
int main(void)
{
INT num1 = 120; // int num1 = 120;
PTR_INT pnum1 = &num1; // int * pnum1 = &num1;
UINT num2 = 190; // unsinged int num2 = 190;
PTR_UINT pnum2 = &num2; // unsigend int * pnum2 = &num2;
UCHAR ch = 'Z'; // unsigned char ch = 'Z';
PTR_UCHAR pch = &ch; // unsigned char * pch = &ch;
printf("%d, %u, %c\n", *pnum1, *pnum2, *pch);
return 0;
}
/* 결과
120, 190, Z
*/
구조체의 정의와 typedef 선언
// 구조체 변수의 선언에 있어서 struct 선언을 생략할 수 있는 방법
struct point
{
int xpos;
int ypos;
};
typedef struct point Point; // struct point에 Point라는 이름을 부여!
// 이는 struct point를 대신할 수 있도록 Point라는 이름을 정의한 셈이니, 이후로는 다음과 같이 struct 선언을 생략한 형태로 구조체 변수를 선언할 수 있다
Point pos;
// 때문에 구조체의 정의와 typedef의 선언을 한 데 묶을 수 있고, 이게 일반적인 선언 방식이다.
typedef struct point
{
int xpos;
int ypos;
} Point;
#include <stdio.h>
struct point
{
int xpos;
int ypos;
};
typedef struct point Point;
typedef struct person
{
char name[20];
char phoneNum[20];
int age;
} Person;
int main(void)
{
Point pos={10, 20};
Person man={"이승기", "010-1212-0001", 21};
printf("%d %d\n", pos.xpos, pos.ypos);
printf("%s %s %d\n", man.name, man.phoneNum, man.age);
return 0;
}
/* 결과
10 20
이승기 010-1212-0001 21
*/
구조체의 이름 생략
// 앞서 보인 예제에서는 구조체 person을 다음의 형태로 정의
typedef struct person
{
char name[20];
char phoneNum[20];
int age;
} Person;
/*
이렇게 정의가 되면, 구조체의 이름 person은 사실상 별 의미를 갖지 않게 됌
따라서, 다음과 같이 구조체의 이름을 생략하는 것도 가능
*/
typedef struct
{
char name[20];
char phoneNum[20];
int age;
} Person;
함수로의 구조체 변수 전달과 반환
함수의 인자로 전달되고 return문에 의해 반환되는 구조체 변수
void SimpleFunc(int num) { . . . . }
int main(void)
{
int age=24;
SimpleFunc(age); // age에 저장된 값이 매개뱐수 num에 전달(복사)
. . . .
}
#include <stdio.h>
typedef struct
{
int xpos;
int ypos;
} Point;
void ShowPosition(Point pos)
{
printf("[%d, %d]\n", pos.xpos, pos.ypos);
}
Point GetCurrentPosition(void)
{
Point cen;
printf("Input current pos: ");
scanf("%d %d", &cen.xpos, &cen.ypos);
return cen;
}
int main(void)
{
Point curPos=GetCurrentPosition();
ShowPosition(curPos);
return 0;
}
/* 결과
Input current pos: 2 4
[2, 4]
*/
#include <stdio.h>
typedef struct
{
char name[20];
char phoneNum[20];
int age;
} Person;
void ShowPersonInfo(Person man)
{
printf("name: %s\n", man.name);
printf("phone: %s\n", man.phoneNum);
printf("age: %d\n", man.age);
}
Person ReadPersonInfo(void)
{
Person man;
printf("name? "); scanf("%s", man.name);
printf("phone? "); scanf("%s", man.phoneNum);
printf("age? "); scanf("%d", &man.age);
return man;
}
int main(void)
{
Person man=ReadPersonInfo();
ShowPersonInfo(man);
return 0;
}
/* 결과
name? Jung
phone? 010-12XX-34XX
age? 22
name: Jung
phone: 010-12XX-34XX
age: 22
*/
#include <stdio.h>
typedef struct
{
int xpos;
int ypos;
} Point;
void OrgSymTrans(Point * ptr) // 원점대칭
{
ptr->xpos = (ptr->xpos) * -1;
ptr->ypos = (ptr->ypos) * -1;
}
void ShowPosition(Point pos)
{
printf("[%d, %d]\n", pos.xpos, pos.ypos);
}
int main(void)
{
Point pos={7, -5};
OrgSymTrans(&pos); // pos의 값을 원점 대칭이동시킨다.
ShowPosition(pos);
OrgSymTrans(&pos); // pos의 값을 원점 대칭이동시킨다.
ShowPosition(pos);
return 0;
}
/* 결과
[-7, 5]
[7, -5]
*/
구조체 변수를 대상으로 가능한 연산
구조체 변수를 대상으로는 매우 제한된 형태의 연산만 허용
허용되는 가장 대표적인 연산은 대입연산이며, 그 이외로 주소 값 반환을 목적으로 하는 & 연산이나 구조체 변수의 크기를 반환하는 sizeof 정도의 연산만 허용
#include <stdio.h>
typedef struct
{
int xpos;
int ypos;
} Point;
int main(void)
{
Point pos1={1, 2};
Point pos2;
pos2=pos1; // pos1의 멤버 대 pos2의 멤버 간 복사가 진행됨
printf("크기: %lu\n", sizeof(pos1)); // pos1의 전체 크기 반환
printf("[%d, %d]\n", pos1.xpos, pos1.ypos);
printf("크기: %lu\n", sizeof(pos2)); // pos2의 전체 크기 반환
printf("[%d, %d]\n", pos2.xpos, pos2.ypos);
return 0;
}
/* 결과
크기: 8
[1, 2]
크기: 8
[1, 2]
*/
구조체 안에는 배열도 존재할 수 있고 포인터 변수도 존재할 수 있다. 뿐만 아니라 다른 구조체의 변수도 존재할 수 있다.
따라서 구조체 변수 대상의 덧셈과 뺄셈을 하려면 함수를 정의해야 한다.
#include <stdio.h>
typedef struct
{
int xpos;
int ypos;
} Point;
Point AddPoint(Point pos1, Point pos2)
{
Point pos={pos1.xpos+pos2.xpos, pos1.ypos+pos2.ypos};
return pos;
}
Point MinPoint(Point pos1, Point pos2)
{
Point pos={pos1.xpos-pos2.xpos, pos1.ypos-pos2.ypos};
return pos;
}
int main(void)
{
Point pos1={5, 6};
Point pos2={2, 9};
Point result;
result = AddPoint(pos1, pos2);
printf("[%d, %d]\n", result.xpos, result.ypos);
result = MinPoint(pos1, pos2);
printf("[%d, %d]\n", result.xpos, result.ypos);
return 0;
}
/* 결과
[7, 15]
[3, -3]
*/
구조체의 유용함에 대한 논의와 중첩 구조체
구조체를 정의하는 이유
“구조체를 통해서 연관 있는 데이터를 하나로 묶을 수 있는 자료형을 정의하면, 데이터의 표현 및 관리가 용이해지고, 그만큼 합리적인 코드를 작성할 수 있게 된다.”
#include <stdio.h>
typedef struct
{
char name[20]; // 학생 이름
char stdnum[20]; // 학생 고유번호
char school[20]; // 학교 이름
char major[20]; // 선택 전공
int year; // 학년
} Student;
void ShowStudentInfo(Student * sptr)
{
printf("학생 이름: %s\n", sptr->name);
printf("학생 고유번호: %s\n", sptr->stdnum);
printf("학교 이름: %s\n", sptr->school);
printf("선택 전공: %s\n", sptr->major);
printf("학년: %d\n", sptr->year);
}
int main(void)
{
Student arr[7];
int i;
for(i=0 ; i<7 ; i++)
{
printf("이름: "); scanf("%s", arr[i].name);
printf("번호: "); scanf("%s", arr[i].stdnum);
printf("학교: "); scanf("%s", arr[i].school);
printf("전공: "); scanf("%s", arr[i].major);
printf("학년: "); scanf("%d", &arr[i].year);
}
for(i=0 ; i<7 ; i++)
ShowStudentInfo(&arr[i]);
return 0;
}
중첩된 구조체의 정의와 변수의 선언
구조체 안에 구조체 변수가 멤버로 존재하는 경우를 가리켜 ‘구조체의 중첩’이라 한다.
#include <stdio.h>
typedef struct
{
int xpos;
int ypos;
} Point;
typedef struct
{
Point cen;
double rad;
} Circle;
void ShowCircleInfo(Circle * cptr)
{
printf("[%d, %d]\n", (cptr->cen).xpos, (cptr->cen).ypos);
printf("radius: %g \n\n", cptr->rad);
}
int main(void)
{
Circle c1={{1, 2}, 3.5};
Circle c2={2, 4, 3.9};
ShowCircleInfo(&c1);
ShowCircleInfo(&c2);
return 0;
}
/* 결과
[1, 2]
radius: 3.5
[2, 4]
radius: 3.9
*/
공용체(Union Type)의 정의와 의미
구조체 vs 공용체
typedef struct sbox // 구조체 sbox의 정의
{
int mem1;
int mem2;
double mem3;
} SBox;
typedef union ubox // 공용체 ubox의 정의
{
int mem1;
int mem2;
double mem3;
} UBox;
/* 위의 구조체와 공용체를 대상으로 각각 다음 연산을 하면, */
printf("%d \n", sizeof(SBox)); // 16 출력
printf("%d \n", sizeof(UBox)); // 8 출력
/* 여기서 16은 모든 멤버의 크기를 합한 결과이고, 8은 멤버 중에서 가장 크기가 큰 double의 크기만 계산된 결과 */
#include <stdio.h>
typedef struct sbox // 구조체 sbox의 정의
{
int mem1;
int mem2;
double mem3;
} SBox;
typedef union ubox // 공용체 ubox의 정의
{
int mem1;
int mem2;
double mem3;
} UBox;
int main(void)
{
SBox sbx;
UBox ubx;
printf("%p %p %p\n", &sbx.mem1, &sbx.mem2, &sbx.mem3);
printf("%p %p %p\n", &ubx.mem1, &ubx.mem2, &ubx.mem3);
printf("%lu %lu\n", sizeof(SBox), sizeof(UBox));
return 0;
}
/* 결과
0x16f5a2f58 0x16f5a2f5c 0x16f5a2f60
0x16f5a2f50 0x16f5a2f50 0x16f5a2f50
16 8
UBox형 변수를 구성하는 멤버 mem1, mem2, mem3의 주소 값이 동일하다
공용체 변수가 선언되면, 공용체를 구성하는 멤버는 각각 할당되지 않고, 그 중 크기가 가장 큰 멤버의 변수만 하나 할당되어
공유하게 된다.
*/
#include <stdio.h>
typedef union ubox // 공용체 ubox의 정의
{
int mem1;
int mem2;
double mem3;
} UBox;
int main(void)
{
UBox ubx; // 8 바이트 메모리 할당
ubx.mem1=20; // 상위 4 바이트의 메모리 공간에 20을 저장
printf("%d\n", ubx.mem2);
/* mem2는 int형 변수이므로 이 이름으로 접근할 경우 상위 4바이트의 메모리 공간을 참조하게 된다. 그런데 앞서 이 공간에 20을 저장했으므로 20이 출력*/
ubx.mem3=7.15;
/* mem3에 실수를 저장, 결과적으로 13행을 통해 저장된 값을 덮어써버리게 됌 */
printf("%d\n", ubx.mem1);
printf("%d\n", ubx.mem2);
/* 실수를 저장하면서 덮어버렸기 때문에, 상위 4 바이트를 읽어서 출력하면 알 수 없는 값이 출력 */
printf("%g\n", ubx.mem3);
return 0;
}
/* 결과
20
-1717986918
-1717986918
7.15
*/
공용체의 유용함은 다양한 접근방식을 제공하는 데 있습니다.
공용체의 유용함은 ‘하나의 메모리 공간을 둘 이상의 방식으로 접근할 수 있다'
/* 상위 2 바이트와 하위 2 바이트 값을 양의 정수, 상위 1 바이트와 하위 1 바이트에 저장된 값의 아스키 문자 */
#include <stdio.h>
typedef struct
{
unsigned short upper;
unsigned short lower;
} DBShort;
typedef union
{
int iBuf;
char bBuf[4];
DBShort sBuf;
} RDBuf;
int main(void)
{
RDBuf buf;
printf("정수 입력: ");
scanf("%d", &(buf.iBuf));
printf("상위 2 바이트: %u\n", buf.sBuf.upper);
printf("하위 2 바이트: %u\n", buf.sBuf.lower);
printf("상위 1 바이트 아스키 코드: %c\n", buf.bBuf[0]);
printf("하위 1 바이트 아스키 코드: %c\n", buf.bBuf[3]);
return 0;
}
/* 결과
정수 입력: 1145258561
상위 2 바이트: 16961
하위 2 바이트: 17475
상위 1 바이트 아스키 코드: A
하위 1 바이트 아스키 코드: D
공용체의 적절한 정의를 통해서 4 바이트 메모리 공간을 2 바이트씩, 그리고 1 바이트씩 접근할 수 있다.
*/
열거형의 정의와 의미
열거형의 정의와 변수의 선언
/* 열거형으로 syllable이라는 이름의 자료형을 정의한다는 것은 다음의 의미를 지닌다.
"syllable형 변수에 저장이 가능한 정수 값들을 결정하겠다!"
열거형의 경우에는 저장이 가능한 값 자체를 정수의 형태로 결정 */
enum syllable // syllable이라는 이름의 열거형 정의
{
Do=1, Re=2, Mi=3, Fa=4, So=5, La=6, Ti=7
};
/*
"Do를 정수 1을 의미하는 상수로 정의, 그리고 이 값은 syllable형 변수에 저장이 가능"
*/
#include <stdio.h>
typedef enum syllable // typedef 선언이 추가된 열거형의 정의
{
Do=1, Re=2, Mi=3, Fa=4, So=5, La=6, Ti=7
} Syllable;
void Sound(Syllable sy)
{
switch(sy)
{
case Do:
puts("도는 하얀 도라지"); return;
case Re:
puts("레는 둥근 레코드"); return;
case Mi:
puts("미는 파란 미나리"); return;
case Fa:
puts("파는 예쁜 파랑새"); return;
case So:
puts("솔은 작은 솔방울"); return;
case La:
puts("라는 라디오고요~"); return;
case Ti:
puts("시는 졸졸 시냇물"); return;
}
puts("다 함께 부르세~ 도레미파 솔라시도 홀 도~ 짠~");
}
int main(void)
{
Syllable tone;
for(tone=Do ; tone<=Ti ; tone++)
Sound(tone);
return 0;
}
/* 결과
도는 하얀 도라지
레는 둥근 레코드
미는 파란 미나리
파는 예쁜 파랑새
솔은 작은 솔방울
라는 라디오고요~
시는 졸졸 시냇물
위 반복문에서 Do와 Ti는 각각 1과 7을 의미하는 상수이니
for(tone=1 ; tone<=7 ; tone++)
로 대신해도 그 결과는 동일하다.
*/
열거형 상수의 값이 결정되는 방식
열거형을 정의하는데 있어서 상수의 값을 명시하지 않으면
enum color {RED, BLUE, WHITE, BLACK};
/* 위 정의에는 상수의 이름만 선언되었을 뿐 값은 선언되어 있지 않다. 이러한 경우 열거형 상수의 값은 0에서부터 1씩 증가하는 형태로 결정 */
enum color {RED=0, BLUE=1, WHITE=2, BLACK=3}; // 위와 동일
/* 이번에는 정의 형태가 조금 다른 예를 보자. */
enum color {RED=3, BLUE, WHITE=6, BLACK};
/* 값이 선언되지 않으면 앞서 선언된 상수보다 1이 증가된 값이 할당 */
enum color {RED=3, BLUE=4, WHITE=6, BLACK=7}; // 위와 동일