Programming/C

[C] union(공용체) 이란

임아톰 2023. 3. 17. 13:24

union(공용체)이란

C언어에서 union은 하나의 메모리 공간을 여러 가지 방식으로 해석할 수 있도록 하는 데이터 타입입니다. 이는 구조체와 유사하지만 구조체는 각 멤버 변수가 메모리 공간을 따로 사용하는 반면, union은 모든 멤버 변수가 같은 메모리 공간을 공유합니다.

 

실제 사용 예시는 다음과 같습니다. ADC_CONFIG는 구조체이고 HADC는 공용체입니다.

#include <stdio.h>

typedef struct
{
    unsigned char CNFG1;
    unsigned char CNFG2;
    unsigned short MODE;
} ADC_CONFIG;

typedef union
{
    ADC_CONFIG      cnfg;
    unsigned char   byte[4];
} HADC;

int main(){
    ADC_CONFIG adc1 = {0x01, 0x80, 0xF000};
    printf("strct ADC_CONFIG size: %d\n", sizeof(ADC_CONFIG));

    HADC a1;
    printf("union HADC size: %d\n", sizeof(a1));

    return 0;
}

main문에서 구조체 adc1과 공용체 a1의 크기를 출력하였습니다. 결과는 아래와 같습니다.

strct ADC_CONFIG size: 4
union HADC size: 4

공용체는 메모리 공간을 공유하기 때문에 메모리 크기의 변화가 없습니다. 공용체의 메모리 크기는 가장 큰 자료형에 의해 결정됩니다. ADC_CONFIG의 크기와 unsigned char byte[4]의 크기는 모두 4바이트로 동일하고 따라서 공용체의 크기도 4가 됩니다.

 

차이점은 공용체를 사용하면 아래 코드와 같은 방식으로 메모리 접근이 가능합니다.

#include <stdio.h>

typedef struct
{
    unsigned char CNFG1;
    unsigned char CNFG2;
    unsigned short MODE;
} ADC_CONFIG;

typedef union
{
    ADC_CONFIG      cnfg;
    unsigned char   byte[4];
} HADC;


int main(){
    ADC_CONFIG adc1 = {0x01, 0x80, 0xF000};
    printf("strct ADC_CONFIG size: %d\n", sizeof(ADC_CONFIG));

    HADC a1;
    printf("union HADC size: %d\n", sizeof(a1));

    // cnfg를 통한 메모리 접근
    a1.cnfg.CNFG1 = 0x11;
    a1.cnfg.CNFG2 = 0x22;
    a1.cnfg.MODE = 0xFBCD;
    printf("CNFG1: 0x%X, CNFG2: 0x%X, MODE: 0x%X\n", a1.cnfg.CNFG1, a1.cnfg.CNFG2, a1.cnfg.MODE);

    // bytep[4]를 통한 바이트 단위 메모리 접근
    a1.byte[0] = 0x33;
    a1.byte[1] = 0x44;
    a1.byte[2] = 0x55;
    a1.byte[3] = 0x66;
    printf("CNFG1: 0x%X, CNFG2: 0x%X, MODE: 0x%X\n", a1.cnfg.CNFG1, a1.cnfg.CNFG2, a1.cnfg.MODE);


    return 0;
}

즉, 구조체로 메모리에 접근할 수도 있고 바이트 단위로 메모리에 접근하는 것도 가능합니다.

 

임베디드 시스템에서의 union(공용체) 사용

union은 임베디드 시스템에서 레지스터 주소를 정의할 때 사용됩니다. 예시와 함꼐 살펴보겠습니다.

/** \brief  10, Port Input/Output Control Register 0 */
#define P10_IOCR0 /*lint --e(923)*/ (*(volatile Ifx_P_IOCR0*)0xF003B010u)

/** \brief  Port Input/Output Control Register 0 */
typedef union
{
    unsigned int U;                         /**< \brief Unsigned access */
    signed int I;                           /**< \brief Signed access */
    Ifx_P_IOCR0_Bits B;                     /**< \brief Bitfield access */
} Ifx_P_IOCR0;

/** \brief  Port Input/Output Control Register 0 */
typedef struct _Ifx_P_IOCR0_Bits
{
    unsigned int reserved_0:3;              /**< \brief \internal Reserved */
    unsigned int PC0:5;                     /**< \brief [7:3]  (rw) */
    unsigned int reserved_8:3;              /**< \brief \internal Reserved */
    unsigned int PC1:5;                     /**< \brief [15:11]  (rw) */
    unsigned int reserved_16:3;             /**< \brief \internal Reserved */
    unsigned int PC2:5;                     /**< \brief [23:19]  (rw) */
    unsigned int reserved_24:3;             /**< \brief \internal Reserved */
    unsigned int PC3:5;                     /**< \brief [31:27]  (rw) */
} Ifx_P_IOCR0_Bits;

위의 예시에서 Ifx_P_IOCR0는 union으로 정의되어 있어서 U, I 혹은 B로 메모리에 접근할 수 있습니다. 

 

Ifx_P_IOCR0_Bits는 비트 필드를 사용하여 비트 단위로 멤버를 만들었습니다.Ifx_P_IOCR0_Bits는 아래와 같은 레지스터 구조를 코드로 나타낸 것입니다.

처음 3비트는 reserved_0으로 그 다음 5비트는 PC0로, 그 다음 3비트는 reserved_1으로 다음 5비트는 PC1, 그 다음 3비트는 reserved_2으로 다음 5비트는 PC2, 그 다음 3비트는 reserved_3으로 다음 5비트는 PC3으로 정의됐습니다. 

 

이렇게 정의한 경우 아래 코드와 같이 특정 비트 영역을 접근하는 게 가능합니다.

/** \brief  10, Port Input/Output Control Register 0 */
#define P10_IOCR0 /*lint --e(923)*/ (*(volatile Ifx_P_IOCR0*)0xF003B010u)

/** \brief  Port Input/Output Control Register 0 */
typedef union
{
    unsigned int U;                         /**< \brief Unsigned access */
    signed int I;                           /**< \brief Signed access */
    Ifx_P_IOCR0_Bits B;                     /**< \brief Bitfield access */
} Ifx_P_IOCR0;

/** \brief  Port Input/Output Control Register 0 */
typedef struct _Ifx_P_IOCR0_Bits
{
    unsigned int reserved_0:3;              /**< \brief \internal Reserved */
    unsigned int PC0:5;                     /**< \brief [7:3]  (rw) */
    unsigned int reserved_8:3;              /**< \brief \internal Reserved */
    unsigned int PC1:5;                     /**< \brief [15:11]  (rw) */
    unsigned int reserved_16:3;             /**< \brief \internal Reserved */
    unsigned int PC2:5;                     /**< \brief [23:19]  (rw) */
    unsigned int reserved_24:3;             /**< \brief \internal Reserved */
    unsigned int PC3:5;                     /**< \brief [31:27]  (rw) */
} Ifx_P_IOCR0_Bits;

int main(){
    P10_IOCR0.B.PC0 = 0x10;
    P10_IOCR0.B.PC1 = 0x10;
    P10_IOCR0.B.PC2 = 0x10;
    P10_IOCR0.B.PC3 = 0x10;
    
    return 0;
}

임베디드 시스템에서는 하드웨어 모듈을 사용하기 위해 특정 메모리 영역의 특정 비트에 접근할 일이 많기 때문에 다음과 같이 정의하여 사용하는 경우가 많습니다.

반응형