Unit 54 공용체 사용하기
-공용체-
공용체는 구조체와 정의 방법은 같지만 멤버를 저장하는 방식이 다른 자료형이다. 구조체는 멤버들이 각각 공간을 차지하지만 공용체는 모든 멤버가 같은 공간을 공유한다.
공용체는 구조체와 달리 union 키워드로 정의하지만 형태는 구조체와 같다.
union <공용체 명>
{
<멤버들>
};
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
typedef union uni {
char a[8]; // 8byte
int b; // 4byte
long long c; // 8byte
} A; // 공용체 선언
int main()
{
A a; // 공용체 변수 선언
printf("%d\n", sizeof(a)); // 공용체 크기 출력
strcpy(a.a, "abcd"); // a.a에 abcd 저장
printf("%s\n", a.a);
printf("%d\n", a.b);
printf("%lld\n", a.c);
// 공용체 멤버들 출력
}
위는 공용체를 선언 및 공용체의 크기를 출력하고 멤버에 값을 저장한 후 멤버들을 출력하는 코드다.
실행 결과를 보면 공용체의 크기는 8byte밖에 안되고 멤버 a는 정상적으로 출력이 됐지만 다른 멤버들은 저장된 값이 이상하게 변해버린 것을 볼 수 있다.
일단 크기가 8byte인 이유는 공용체는 공간을 공유하기 때문에 가장 큰 자료형의 공간을 확보한 뒤 그 공간을 여러 멤버들이 나눠쓰기 때문이다. 따라서 한 멤버에 값을 넣으면 같은 공간을 사용하는 다른 멤버들의 값이 이상하게 변해버리는 것이다.
결론적으로는 공용체의 멤버는 한 번에 하나씩 써야지 정상적으로 사용할 수 있는 것 같다.
실무에서는 공용체에 값을 저장할 때 어떤 멤버를 사용할 것인지 미리 정해놓고, 꺼낼 대도 정해놓은 멤버에서 값을 꺼내는 식으로 사용된다고 한다.
내 생각이지만 이 공용체는 최적화에 많이 사용할 것 같다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
typedef union uni {
int a;
short b;
char c;
} A; // 공용체 선언
int main()
{
A a; // 공용체 변수 선언
a.a = 0x12345678; // a에 0x12345678 저장
printf("%x\n", a.a);
printf("%x\n", a.b);
printf("%x\n", a.c);
// 공용체 멤버들 출력
}
위는 공용체를 선언하고 멤버 중 a에 0x12345678을 저장한 후 모든 멤버들의 16진수 값을 출력하는 코드다.
출력 결과를 보면 예상과는 달리 나온다. a는 정상적으로 출력되는데 b는 5678, c는 78 이렇게 거꾸로 출력을 한다. 이렇게 되는 이유는 메모리에 값을 리틀 엔디언으로 저장하기 때문인데, 0x12345678의 경우에는 메모리에 0x78 0x 56 0x 34 0x 12로 저장한다는 뜻이다. 따라서 b는 2바이트기 때문에 0x78 0x56을 읽어서 5678을 c는 1바이트기 때문에 0x78을 읽어서 78만 출력해주는 것이다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
typedef union uni {
int a;
short b;
char c;
} A; // 공용체 선언
int main()
{
A *a = malloc(sizeof(A)); // 공용체 포인터 변수 선언 및 메모리 할당
a->a = 0x12345678; // a에 0x12345678 저장
printf("%x\n", a->a);
printf("%x\n", a->b);
printf("%x\n", a->c);
// 공용체 멤버들 출력
free(a); // 메모리 해제
}
위 코드처럼 malloc과 포인터 변수를 이용해 공용체의 메모리를 동적할당할 수 있다. 마찬가지로 값에 접근할 때는 .이 아닌 ->로 해줘야한다.
제대로 값이 저장되고 출력되는 것을 볼 수 있다.
Unit 55 구조체와 공용체 사용하기
-구조체, 공용체 활용-
구조체는 구조체를 멤버로 가질 수 있다.
#include <stdio.h>
struct a {
int x;
char y;
};
struct b {
char q;
int w;
struct a a1; // a 구조체 변수
};
// 구조체 2개 정의
int main()
{
struct b b1;
b1.a1.x = 1; // 구조체 변수 b1의 구조체 변수 a1의 y에 2를 저
b1.a1.y = 2; // 구조체 변수 b1의 구조체 변수 a1의 y에 2를 저장
printf("%d %d\n", b1.a1.x, b1.a1.y); // a 구조체의 멤버 출력
return 0;
}
위는 구조체 안에 구조체 변수를 넣고 멤버에 접근한 코드다. 구조체 안의 구조체 변수의 멤버에 접근할 때는 <구조체 변수>.<구조체 안의 구조체 변수>.<멤버>로 하면 된다.
실행 결과를 보면 1, 2가 잘 출력되는 것을 볼 수 있다.
위는 구조체 안에 구조체 변수를 선언했지만
struct b {
char q;
int w;
struct a {
int x;
char y;
} a1;
};
위처럼 구조체 안에 구조체를 선언할 수도 있다.
#include <stdio.h>
struct a {
int x;
char y;
};
struct b {
char q;
int w;
struct a a1; // a 구조체 변수
};
// 구조체 2개 정의
int main()
{
struct b *b1 = malloc(sizeof(struct b)); // 구조체 포인터 선언 및 메모리 할당
b1->a1.x = 1; // 구조체 포인터 변수 b1의 구조체 변수 a1의 y에 2를 저
b1->a1.y = 2; // 구조체 포인터 변수 b1의 구조체 변수 a1의 y에 2를 저장
printf("%d %d\n", b1->a1.x, b1->a1.y); // a 구조체의 멤버 출력
free(b1);
return 0;
}
위는 구조체 안의 구조체 변수 코드를 포인터로 선언하고 메모리 할당을 한 모습이다. 접근은 포인터 -> 멤버.멤버로 하면 된다.
마찬가지로 1, 2가 잘 출력되는 것을 볼 수 있다.
점이냐 화살표냐 구분할 때는 현재 구조체가 선언된 상태를 확인하면 된다. 변수면 점, 포인터면 화살표로 기억하면 된다.
#include <stdio.h>
struct a {
int x;
char y;
};
struct b {
char q;
int w;
struct a *a1; // a 구조체 포인터
};
// 구조체 2개 정의
int main()
{
struct b *b1 = malloc(sizeof(struct b)); // 구조체 포인터 선언 및 메모리 할당
b1->a1 = malloc(sizeof(struct a)); // 구조체 포인터 안 구조체 포인터에도 메모리 할당
b1->a1->x = 1; // 구조체 포인터 변수 b1의 구조체 변수 a1의 y에 2를 저
b1->a1->y = 2; // 구조체 포인터 변수 b1의 구조체 변수 a1의 y에 2를 저장
printf("%d %d\n", b1->a1->x, b1->a1->y); // a 구조체의 멤버 출력
free(b1->a1);
free(b1);
// 메모리 해제
return 0;
}
위는 구조체 안에 구조체 포인터가 있는 경우다. 이럴 경우 구조체 변수나 포인터를 선언하고 해당 포인터 변수에 malloc 함수로 메모리를 할당해주면 된다. 단 접근할 때는 ->로 해줘야한다.
또한 free로 메모리 해제를 해주는 것을 잊으면 안된다.
마찬가지로 실행 결과는 1, 2가 출력된다.
-익명 구조체, 익명 공용체 활용
#include <stdio.h>
struct Vector3 { // 3차원 벡터 좌표
union { // 익명 공용체
struct { // 익명 구조체
float x;
float y;
float z;
// x, y, z 멤버
};
float v[3]; // 3 변수를 배열로 접근
};
};
int main()
{
struct Vector3 pos;
for (int i = 0; i < 3; i++)
{
pos.v[i] = 1.0f;
}
// 3번 반복하며 x, y, z에 1.0f을 저장
printf("%f %f %f\n", pos.x, pos.y, pos.z);
// x, y, z로 접근
return 0;
}
위는 익명 구조체, 공용체를 활용해 백터 좌표를 만드는 코딩도장의 예시코드다.
공용체는 공간을 나눈다는 점을 이용해 x, y, z 3 변수와 3 크기의 배열을 선언해 배열을 통해 각 멤버에 접근할 수 있도록 했고 이를 감싸는 Vector3라는 구조체를 정의했다.
익명 공용체의 배열을 통해 x, y, z 멤버에 접근가능하며 .을 통해서 각 멤버에 접근할 수 있다.
결과를 보면 for문으로 넣은 1.0이 잘 출력되는 것을 볼 수 있다.
Unit 54 ~ Unit 55 문제
공용체의 크기는 가장 큰 자료형의 크기가 된다. 위 문제의 경우는 8byte가 된다.
답 : 8
공용체 포인터를 선언하고 메모리를 할당하는 방법은 union <공용체 명> *<변수 명> = malloc(sizeof(union <공용체 명>));으로 해주면 된다.
공용체는 같은 공간을 share해서 쓰기 때문에 num2에 저장된 0x11111111 중 2byte 만큼이 num1 값이 된다.
답 : 0x1111
num1에는 0x5678을, c1에 0x78이 들어가게끔 공용체를 정의하는 것이 목적이다. 공용체는 한 공간을 share해서 쓰기 때문에 이 점을 이용하면 될 것 같다. 여기서 num1은 2byte 크기인 short, c1은 1byte 크기인 char형으로 만들면 되겠다.
#include <stdio.h>
union Data {
char c1;
short num1;
}; // 공용체 정의
int main()
{
union Data d1; // 공용체 변수 선언
d1.num1 = 0x5678 // 공용체 변수 d1의 멤버 num1에 0x5678을 저장
printf("0x%x 0x%x\n", d1.num1, d1.c1); // 공용체 변수 d1의 멤버 num1, c1을 출력
return 0;
}
공용체 포인터 변수를 선언 후 메모리를 동적 할당하는 것이 목적이다. 포인터 변수를 선언한 후 malloc 함수로 메모리를 할당해주면 되겠다.
#include <stdio.h>
#include <stdlib.h>
union Data {
short num1;
int num2;
};
int main()
{
union Data *d1 = malloc(sizeof(union Data)); // 포인터 변수 선언 후 malloc으로 메모리 동적할당
d1->num2 = 1;
printf("%d %d\n", d1->num1, d1->num2);
free(d1);
return 0;
}
크기가 4인 공용체를 선언하는 것이 목적이다. 이미 num1, c1이 쓰였으므로 num1은 int, c1은 char로 정의하면 가장 큰 자료형인 int를 기준으로 4byte 크기의 공용체가 만들어질 것이다.
#include <stdio.h>
union Data
{
int num1;
char c1;
}; // 4byte 크기의 공용체 선언
int main()
{
union Data d1;
d1.num1 = 0x1111;
printf("0x%x %d\n", d1.c1, sizeof(d1));
return 0;
}
공용체의 특징을 이용해 0x1111 0x11111111을 출력하는 것이 목적이다. 근데 free 함수가 있는 것을 보아 메모리 동적할당을 해줘야할 것 같다.
#include <stdio.h>
#include <stdlib.h>
union Data {
short num1;
int num2;
};
int main()
{
union Data *d1 = malloc(sizeof(union Data)); // 공용체 선언 및 메모리 동적할당
d1->num2 = 0x11111111; // 공용체 변수 d1의 멤버 num2에 0x11111111을 저장
printf("0x%x 0x%x\n", d1->num1, d1->num2);
free(d1);
return 0;
}
malloc으로 메모리 동적 할당을 해준 후 int형인 num2에 0x11111111을 저장하도록 작성했다.
구조체 변수 p1 안의 구조체 변수 phone의 멤버 number에 접근하는 것이므로 p1.phone.number로 접근하면 된다.
답 : d
포인터 변수 p1에서 number 멤버에 접근하는 방법은 p1->phone.number로 해주면 된다.
답 : e
포인터 변수 p1에서 포인터 변수 phone를 통해 number 멤버에 접근하는 방법은 p1->phone->number로 접근하면 된다.
답 : b
익명 공용체와 구조체를 활용한 Vector3다. 이는 pos.배열 v의 1 인덱스를 통해 접근하거나 pos.x로도 접근할 수 있다.
답 : d
코드를 보니 Champion 구조체 안의 stats라는 Stats 구조체 변수에 접근해서 값을 넣는 것이다. 따라서 빈칸에 Stats의 구조체 변수를 작성해주면 될 것 같다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
struct Stats {
float health;
float healthRegen;
unsigned int mana;
float manaRegen;
float range;
float attackDamage;
float armor;
float attackSpeed;
float magicResist;
unsigned int movementSpeed;
};
struct Champion {
char name[20];
struct Stats stats; // 구조체 변수 정의
float abilityPower;
};
int main()
{
struct Champion lux;
strcpy(lux.name, "Lux");
lux.stats.health = 477.72f;
lux.stats.healthRegen = 1.08f;
lux.stats.mana = 334;
lux.stats.manaRegen = 1.24f;
lux.stats.range = 550;
lux.stats.attackDamage = 55.5f;
lux.stats.attackSpeed = 0.625f;
lux.stats.armor = 18.72f;
lux.stats.magicResist = 30;
lux.stats.movementSpeed = 330;
lux.abilityPower = 0;
printf("%u %f\n", lux.stats.mana, lux.stats.manaRegen);
return 0;
}
free 함수가 있는걸 보아 빈 칸에 Champion의 구조체 포인터를 선언하고 malloc으로 메모리를 할당해 주면 될 것 같다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Stats {
float health;
float healthRegen;
unsigned int mana;
float manaRegen;
float range;
float attackDamage;
float armor;
float attackSpeed;
float magicResist;
unsigned int movementSpeed;
};
struct Champion {
char name[20];
struct Stats stats;
float abilityPower;
};
int main()
{
struct Champion *lux = malloc(sizeof(struct Champion));
// 구조체 포인터 선언 및 메모리 할당
strcpy(lux->name, "Lux");
lux->stats.health = 477.72f;
lux->stats.healthRegen = 1.08f;
lux->stats.mana = 334;
lux->stats.manaRegen = 1.24f;
lux->stats.range = 550;
lux->stats.attackDamage = 55.5f;
lux->stats.attackSpeed = 0.625f;
lux->stats.armor = 18.72f;
lux->stats.magicResist = 30;
lux->stats.movementSpeed = 330;
lux->abilityPower = 0;
printf("%u %f\n", lux->stats.mana, lux->stats.manaRegen);
free(lux);
return 0;
}
배열로 멤버에 접근해서 값을 할당하는 것을 보니, 구조체 안에 익명 구조체, 공용체와 배열을 선언하면 될 것 같다.
#include <stdio.h>
struct DeviceOption {
union {
unsigned long long option;
struct {
unsigned char boot[4];
unsigned char interrupt[2];
unsigned char bus[2];
};
}; // 익명 공용체 구조체 선언
};
int main()
{
struct DeviceOption opt;
opt.boot[0] = 0x01;
opt.boot[1] = 0x02;
opt.boot[2] = 0x03;
opt.boot[3] = 0x04;
opt.interrupt[0] = 0x05;
opt.interrupt[1] = 0x06;
opt.bus[0] = 0x07;
opt.bus[1] = 0x11;
printf("0x%llx\n", opt.option);
return 0;
}
Champion 구조체 변수를 선언하고 name 멤버에 Swain을 저장, 구조체 변수 안의 구조체 변수 stats의 health에 463.0을 저장하는 것이 목적이다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
struct Stats {
float health;
float healthRegen;
unsigned int mana;
float manaRegen;
float range;
float attackDamage;
float armor;
float attackSpeed;
float magicResist;
unsigned int movementSpeed;
};
struct Champion {
char name[20];
struct Stats stats;
float abilityPower;
};
int main()
{
struct Champion swain; // 구조체 변수 선언
strcpy(swain.name, "Swain"); // swain의 name 멤버에 접근해 strcpy Swain 저장
swain.stats.health = 463.0f;
// swain 안의 구조체 변수 stats의 멤버 health에 접근해 463.0f 저장
swain.stats.healthRegen = 1.48f;
swain.stats.mana = 290;
swain.stats.manaRegen = 1.49f;
swain.stats.range = 500;
swain.stats.attackDamage = 52.0f;
swain.stats.attackSpeed = 0.625f;
swain.stats.armor = 20.0f;
swain.stats.magicResist = 30;
swain.stats.movementSpeed = 335;
swain.abilityPower = 0;
printf("%s %f\n", swain.name, swain.stats.health);
return 0;
}
구조체 변수를 먼저 선언하고 strcpy 함수로 swain의 name 멤버에 "Swain"을 저장해줬다. 그 후 swain 안의 구조체 변수의 멤버 health에 접근해 463.0f를 넣어줬다.
free가 있는 것을보아 구조체 포인터를 선언하고 메모리를 할당한 후 구조체 포인터 안의 구조체 포인터에도 메모리를 할당해주고 name 멤버, stats.health에 접근해서 "Swain"과 463.0f를 각각 저장해주는 것이 목적이다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Stats {
float health;
float healthRegen;
unsigned int mana;
float manaRegen;
float range;
float attackDamage;
float armor;
float attackSpeed;
float magicResist;
unsigned int movementSpeed;
};
struct Champion {
char name[20];
struct Stats *stats;
float abilityPower;
};
int main()
{
struct Champion *swain = malloc(sizeof(struct Champion)); // 구조체 포인터 선언 및 메모리 할당
swain->stats = malloc(sizeof(struct Stats)); // 구조체 포인터 안의 구조체 포인터에 메모리 할당
strcpy(swain->name, "Swain"); // strcpy 함수로 swain의 name 멤버에 Swain 저장
swain->stats->health = 463.0f; // 구조체 포인터 안의 구조체 포인터의 멤버인 health에 접근해 463.0f 저장
swain->stats->healthRegen = 1.48f;
swain->stats->mana = 290;
swain->stats->manaRegen = 1.49f;
swain->stats->range = 500;
swain->stats->attackDamage = 52.0f;
swain->stats->attackSpeed = 0.625f;
swain->stats->armor = 20.0f;
swain->stats->magicResist = 30;
swain->stats->movementSpeed = 335;
swain->abilityPower = 0;
printf("%s %f\n", swain->name, swain->stats->health);
free(swain->stats);
free(swain);
return 0;
}
구조체 포인터를 선언한 후 malloc 함수를 통해 메모리를 할당해줬다. 구조체 포인터 swain 안의 구조체 포인터 stats에도 역시 malloc을 통해 메모리를 할당해준 후 strcpy 함수로 name 멤버에 접근해 "Swain" 저장, 구조체 포인터 안의 구조체 포인터 멤버 health에 접근해 463.0f를 저장해줬다.
공용체의 성질을 이용해 리틀 엔디안으로 저장된 0x22와 0x11을 option 멤버로 0x1122로 출력하는 것이 목적이다.
DeviceOption 구조체 안에 익명으로 공용체와 구조체를 정의하면 될 것 같다.
#include <stdio.h>
struct DeviceOption
{
union { // 익명 공용체
short option; // short형 변수 option 선언
struct { // 익명 구조체
unsigned char boot;
unsigned char interrupt;
// unsigned char형 boot와 interrupt 선언
};
};
};
int main()
{
struct DeviceOption opt;
opt.boot = 0x22;
opt.interrupt = 0x11;
printf("0x%x\n", opt.option);
return 0;
}
앞서 배운 Vector3를 익명 공용체와 구조체로 만든 것에 아이디어를 받아 만들어봤다. 공용체는 공간을 공유하기 때문에 익명 공용체를 선언하고 short형 option 변수를 선언한 후 그 안에 익명 구조체를 넣고 unsigned char형 두 변수를 선언해준다면 이 두 변수에 저장된 메모리와 같은 곳을 사용하는 option 변수는 0x22 0x11을 리틀엔디언으로 저장됐다 생각해 반대로 0x1122 값을 갖게 될 것이다.
'Old (2021.01 ~ 2021.12) > Programming' 카테고리의 다른 글
c언어 코딩도장 Unit 58 ~ Unit 59 (0) | 2021.04.11 |
---|---|
c언어 코딩도장 Unit 56 ~ Unit 57 (0) | 2021.04.11 |
c언어 코딩도장 Unit 51 ~ Unit 53 (0) | 2021.03.27 |
c언어 코딩도장 Unit 48 ~ Unit 50 (0) | 2021.03.13 |
c언어 코딩도장 Unit 45 ~ Unit 47 (0) | 2021.03.13 |