본문 바로가기
C++/뇌를 자극하는 C++

[C++] 챕터 13 - 복합 타입 : 복합적인 정보

by Minkyu Lee 2023. 6. 14.

// 13-1 : 공용체의 특징 확인

#include <iostream>
using namespace std;

union MyUnion
{
    int i;
    void* p;
};

int main()
{
    MyUnion uni;
   
    // 멤버의 주소확인
    cout << &uni.i << "\n";
    cout << &uni.p << "\n";

    // i에 값을 대입하는 것이 p에도 영향을 미친다.
    uni.i = 0x12345678;
    cout << hex;
    cout << uni.i << "\n";
    cout << uni.p << "\n";

    // p에 값을 대입하는 것이 i에도 영향을 미친다.
    uni.p = (void*)0x87654321; // 실제 코드 작성시 절대 이렇게 하지 말라. 실험을 위해 상수를 주소값인것처럼 형변환하여 넣었다.
    cout << uni.i << "\n";
    cout << uni.p << "\n";

    return 0;
}

/*
--- 결과 ---
0000001A8E8FF688
0000001A8E8FF688

12345678
CCCCCCCC12345678

87654321
0000000087654321

---
// 공용체의 기본
공용체는 사용 빈도가 높지 않다.
구조체와 비슷하게 생겼지만 모든 멤버가 같은 메모리 공간에 자리잡는다.
uni.i주소와 uni.p의 주소가 동일하다.
i에 값을 넣어주면 p도 변한다.

// 공용체의 특징
어느 한쪽의 멤버값 변경이 다른 멤버 값에 영향을 미친다.
3개 이상인 경우도 마찬가지로 모든 멤버가 같은 메모리 공간을 사용한다.

타입마다 크기가 다른 경우는?
공용체 변수의 크기는 가장 큰 크기를 갖는 멤버의 크기와 같다. (제일 큰 것 사용)
*/
 

 

// 13-2 : 열거체의 사용 1

enum {JOB_DWARF, JOB_WARRIOR, JOB_SORCERER}; // 0, 1, 2를 의미한다.

struct Character
{
    int jobType;

    // 다른 멤버들이 더 있다.
};

int main()
{
    Character c;

    // c가 누군가에 의해 초기화 된다.

    if (JOB_SORCERER == c.jobType)
    {
        // 코드 작동
    }

    return 0;
}

/*
--- 결과 ---

---
주석이 없더라도 마법사라는 것을 쉽게 알 수 있다.

// 해석
첫번째 줄은 다음과 동일하다.
enum {JOB_DWARF = 0, JOB_WARRIOR = 1, JOB_SORCERER = 2};
차례대로 0,1,2...가 된다.

// 주의사항
사용자에 의해 초기화 되면 그 이후 심볼들은 그 값을 기반으로 다시 증가한다.
enum {JOB_DWARF, JOB_WARRIOR = 3, JOB_SORCERER}; 와 같이 쓰면,
enum {JOB_DWARF, JOB_WARRIOR = 3, JOB_SORCERER = 4}; 과 동일하다.
*/

 

// 13-3 : 열거체의 사용 2

enum JOB_KINDS { JOB_DWARF, JOB_WARRIOR, JOB_SORCERER }; // 열거체가 JOB_KINDS라는 하나의 타입이 된다.

struct Character
{
    JOB_KINDS jobType;

    // 다른 멤버들이 더 있다.
};

int main()
{
    Character c;

    // c가 누군가에 의해 초기화 된다.

    if (JOB_SORCERER == c.jobType)
    {
        // 코드 작동
    }

    return 0;
}

/*
--- 결과 ---

---
JOB_KINDS 타입은 새로운 정수형 타입이 된다.
열거체는 기본적으론 정수형 타입이다.

하지만 다른점도 많다.
- 덧셈과 같은 연산 수행이 불가하다.
- 정수를 열거체 변수에 대입하는 것 불가하다. (반대는 가능하다)
Color = color2;
color2 = 5; // 오류

열거체 변수에 저장하는 값은 결국 숫자다.
숫자를 해석하는 방식의 차이일 뿐이다.

// 열거자
열거체 안의 하나하나의 심볼을 열거자(Enumerator) 혹은 나열자라고 한다.
*/

 

// 13-4 : 레퍼런스의 기본적인 사용

#include <iostream>
using namespace std;

int main()
{
    int target = 20;
    int& ref = target; // 레퍼런스 변수 정의

    cout << ref << "\n";
    cout << target << "\n";
    cout << &ref << "\n";
    cout << &target << "\n";

    // ref 값 변경
    ref = 100;
    cout << ref << "\n";
    cout << target << "\n";

    return 0;
}

/*
--- 결과 ---
20
20
00000068518FF994
00000068518FF994

100
100

---
// 레퍼런스와 const
레퍼런스는 반드시 초기화해야한다.
처음에 a를 참조하게 지정했다면 죽을때까지 a를 참조한다.

const 속성을 가진 레퍼런스는 추가적 특징을 가진다.
상수에 대한 별명으로 쓰일 수 있다.
const int& rci = 100; // 성공
int& ri = 100; // 실패. const가 아니라서 안된다.

레퍼런스 변수는 상수 바로 참조는 불가하다.
따라서 임시 변수(객체)를 만든 후 레퍼런스가 이 임시 변수를 참조하게 만든다.

const 속성을 가진 레퍼런스는 다른 타입의 변수 참조를 할 수 있다.
char c = 'A';
const int& rci = c;

// typedef
타입에 대한 별명을 만드는데 사용한다.
1. 긴 타입 이름을 짧게 줄여쓴다.
2. 의미를 보다 분명하게 해준다.

/ 사용예시
typedef unsigned char* uc_ptr;
이제 uc_ptr이라고 쓰면 된다.
uc_ptr p = &uc;

// typedef 다른 용도
타입이 바뀔 수 있는 대비용으로 사용 가능하다.

typedef short ID_TYPE
ID_TYPE id1 = (ID_TYPE)0;
ID_TYPE id2 = (ID_TYPE)0;

나중에 아이디 저장하는 변수 타입이 int로 변경될 경우를 위한 준비다.
short로 담을 수 있는 수보다 더 많은 아이디가 필요할 때 int로 바꾸기 위함이다.

*/

 

// 13-5 : 16비트 칼라의 한 점에서 파란색만 추출

#include <bitset>
#include <iostream>
using namespace std;

// 16비트 칼라에서 한 점을 나타내는 구조체
struct Pixel16
{
    // R G B가 각각 5 6 5 를 차지한다.
    unsigned int blue : 5;
    unsigned int green : 6;
    unsigned int red : 5;
};

// unsigned short 타입 값을 Pixel16 구조체로 변환하기 위한 용도다.
union Convert16
{
    Pixel16 pixel;
    unsigned short us;
};

int main()
{
    // 한점의 색상을 보관하는 변수. 임의의 색상 넣었다.
    unsigned short color = 0x1234;

    // Pixel16 구조체 타입인것처럼 다루기 위해 공용체 사용한다.
    Convert16 convert;
    convert.us = color; // 색상 정보 대입한다.

    // 출력
    cout << bitset<16>(color) << "(" << color << ")" << "\n";
    cout << bitset<16>(convert.pixel.blue) << "(" << convert.pixel.blue << ")" << "\n";

    return 0;
}

/*
--- 결과 ---
0001001000110100(4660)
0000000000010100(20)

---
비트필드를 사용하면 구조체의 멤버 메모리 공간 조절할 수 있다.
멤버 a는 3비트만, 멤버 b는 4비트만 차지하게 가능하다.
int a : 3;
int b : 4;

비트필드를 사용하는 멤버는 정수 타입만 가능하다.

사용하지 않는 비트를 넣고 싶을 때는, 멤버의 이름을 적어주지 않으면 된다.
int : 5;

비트 필드는 비트 단위의 논리 연산을 대신해 사용할 수 있다.

// 예제 설명
파란색 값만 추출해내는 예제이다.

// 공용체 형변환
스킵하였다. 아직 이해가 가지 않는다.

// 비트필드 효율적인가?
비트 단위 논리 연산자보다 빠르진 않다.
데이터는 더 적은 메모리를 차지하지만,
코드는 더 많은 메모리를 차지한다.

// 구조체를 포함하는 구조체 등...
여러가지 내용이 책에 있으나 많아서 우선 스킵하였다.
*/

댓글