Managing Bit Fields in C Using Unions and Structs

introduce

In C language, 联合体(union) and 结构体(struct) are two powerful data types that can be used to process different representations of data. This article describes how to use unions and structures to manage bit fields, specifically for 16-bit and 32-bit unsigned integers.

Union

A union is a special data structure that allows you to store different data types at the same memory location. This means that all members of a union share the same memory space. In our case, we define a UNIO_U16union called , which contains a member for storing 16-bit unsigned integers Data_u16and a struct member for bitwise access Bits. The structure Bits defines 16 bit fields, representing each bit of a 16-bit integer.

typedef union 
{
    
    
    uint16_t Data_u16;
    struct  
    {
    
    
        uint16_t Bit0: 1;
        uint16_t Bit1: 1;
        // ...
        uint16_t Bit14: 1;
        uint16_t Bit15: 1;
    } Bits;
} UNIO_U16;

Structure (Struct)

Structs are a way to combine multiple variables of different data types into a custom data type. In our example, we define a UNIO_U32union called , which contains a member for storing 32-bit unsigned integers Data_u32and a structure member Bits for bitwise access. The structure Bits defines 32 bit fields, representing each bit of a 32-bit integer.

typedef union 
{
    
    
    uint32_t Data_u32;
    struct  
    {
    
    
        uint32_t Bit0: 1;
        uint32_t Bit1: 1;
        // ...
        uint32_t Bit30: 1;
        uint32_t Bit31: 1;
    } Bits;
} UNIO_U32;

Examples of using unions and structs

Through unions and structures, we can easily access different bits of integers. For example, bits 0 and 5 of a 16-bit integer can be set with the following code:

	UNIO_U16 u16;
	u16.Bits.Bit0 = 1;
	u16.Bits.Bit5 = 1;
	printf("u16.Data_u16 = %u\n", u16.Data_u16);

Similarly, we can also set bits 0 and 10 of a 32-bit integer:

	UNIO_U32 u32;
	u32.Bits.Bit0 = 1;
	u32.Bits.Bit10 = 1;
	printf("u32.Data_u32 = %u\n", u32.Data_u32);

Define commonly used UNIO_U8, UNIO_U16, UNIO_U32 reference sample code

#include <stdio.h>

#if defined(__GNUC__) // GCC编译器
    #if __x86_64__ || __ppc64__ || __aarch64__ || __mips64 || _M_X64 // 64位编译器判断
        typedef unsigned char uint8_t;
        typedef unsigned short uint16_t;
        typedef unsigned int uint32_t;
    #else // 32位编译器
        typedef unsigned char uint8_t;
        typedef unsigned short uint16_t;
        typedef unsigned long uint32_t;
    #endif
#elif defined(_MSC_VER) // Microsoft Visual C++编译器
    #if defined(_WIN64) // 64位编译器判断
        typedef unsigned char uint8_t;
        typedef unsigned short uint16_t;
        typedef unsigned int uint32_t;
    #else // 32位编译器
        typedef unsigned char uint8_t;
        typedef unsigned short uint16_t;
        typedef unsigned long uint32_t;
    #endif
#else
    #error "Unsupported compiler. Please add compiler-specific checks."
#endif


typedef union 
{
    
    
    uint16_t Data_u16;
    struct  
    {
    
    
        uint8_t LowByte;
        uint8_t HighByte;
    } Bytes;
    struct  
    {
    
    
        uint16_t Bit0: 1;
        uint16_t Bit1: 1;
        uint16_t Bit2: 1;
        uint16_t Bit3: 1;
        uint16_t Bit4: 1;
        uint16_t Bit5: 1;
        uint16_t Bit6: 1;
        uint16_t Bit7: 1;
        
        uint16_t Bit8: 1;
        uint16_t Bit9: 1;
        uint16_t Bit10: 1;
        uint16_t Bit11: 1;
        uint16_t Bit12: 1;
        uint16_t Bit13: 1;
        uint16_t Bit14: 1;
        uint16_t Bit15: 1;
    } Bits;
} UNIO_U16;

typedef union 
{
    
    
    uint32_t Data_u32;
    struct  
    {
    
    
        uint16_t LowWord;
        uint16_t HighWord;
    } Words;
    
    struct  
    {
    
    
        uint8_t Byte0;
        uint8_t Byte1;
        uint8_t Byte2;
        uint8_t Byte3;
    } Bytes;
    struct  
    {
    
    
        uint16_t Bit0: 1;
        uint16_t Bit1: 1;
        uint16_t Bit2: 1;
        uint16_t Bit3: 1;
        uint16_t Bit4: 1;
        uint16_t Bit5: 1;
        uint16_t Bit6: 1;
        uint16_t Bit7: 1;
        
        uint16_t Bit8: 1;
        uint16_t Bit9: 1;
        uint16_t Bit10: 1;
        uint16_t Bit11: 1;
        uint16_t Bit12: 1;
        uint16_t Bit13: 1;
        uint16_t Bit14: 1;
        uint16_t Bit15: 1;
        
        uint16_t Bit16: 1;
        uint16_t Bit17: 1;
        uint16_t Bit18: 1;
        uint16_t Bit19: 1;
        uint16_t Bit20: 1;
        uint16_t Bit21: 1;
        uint16_t Bit22: 1;
        uint16_t Bit23: 1;
        
        uint16_t Bit24: 1;
        uint16_t Bit25: 1;
        uint16_t Bit26: 1;
        uint16_t Bit27: 1;
        uint16_t Bit28: 1;
        uint16_t Bit29: 1;
        uint16_t Bit30: 1;
        uint16_t Bit31: 1;
    } Bits;
} UNIO_U32;

int main()
{
    
    
    UNIO_U16 u16;
    u16.Bits.Bit0 = 1;
    u16.Bits.Bit5 = 1;
    printf("u16.Data_u16 = %u\n", u16.Data_u16);

    UNIO_U32 u32;
    u32.Bits.Bit0 = 1;
    u32.Bits.Bit10 = 1;
    printf("u32.Data_u32 = %u\n", u32.Data_u32);

    return 0;
}

在上述代码中,我们定义了 UNIO_U16 和 UNIO_U32 两个联合体,每个联合体包含一个用于存储整数的成员(Data_u16 和 Data_u32) 以及一个用于按位访问的 Bits 结构体成员。可以根据需要设置每个位的值,并使用 printf 输出整数值。 注意在联合体中,各个成员共享同一段内存空间,因此根据对不同成员的赋值,联合体中存储的整数值也会发生相应的变化。

in conclusion

Unions and structures are powerful tools for manipulating data in C. By using unions to store different types of data, and structs to manage bit fields, we can manipulate data more flexibly and efficiently. This technique is especially useful when dealing with embedded systems and hardware-related programming tasks.

Guess you like

Origin blog.csdn.net/qq_44330858/article/details/131842660