C 语言中的位掩码

Jinku Hu 2023年1月30日 2021年2月7日
  1. 使用 struct 关键字在 C 语言中定义位掩码数据
  2. 使用 struct 结合 union 在 C 语言中定义位掩码数据
C 语言中的位掩码

本文将演示关于如何在 C 语言中使用位掩码的多种方法。

使用 struct 关键字在 C 语言中定义位掩码数据

位掩码通常用于位性操作,以访问或设置位场式数据结构的各个部分。另一方面,利用位域来有效地存储数据,减少内存占用。同时,位智运算在硬件上的执行速度相对比普通的算术运算要快。在下面的例子中,我们使用 struct 关键字来演示位域的实现。

请注意,这是一种特殊的符号,用来构造对象,在这个对象中,可以使用传统的成员访问操作符来检索给定的位域。Bitfield 结构在内存中存储了一个占用 32 位的无符号整数,但它也可以以 3 个不同大小的部分–23 位、5 位和 4 位的值来访问,分别命名为 yeardaymonth。因此,Bitfield 代表了在内存中有效实现的日期抽象。

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>

struct {
    uint32_t year:23;
    uint32_t day:5;
    uint32_t month:4;
} typedef Bitfield;

int main() {
    Bitfield date = {2020, 13,12 };

    printf("sizeof Bitfield: %lu bytes\n", sizeof(date));
    printf("date: %d/%d/%d \n", date.day, date.month, date.year);

    return EXIT_SUCCESS;
}

输出:

sizeof Bitfield: 4 bytes
date: 13/12/2020

使用 struct 结合 union 在 C 语言中定义位掩码数据

另外,我们也可以在前面的结构中加入 union 关键字,这样就可以单独访问整个 32 位数。由于访问位字段成员比访问 struct 的成员要慢,所以我们将使用位运算把日期值分配给单独的 ydm 整数成员。

请注意,代表给定日期的十进制数在逻辑上是相互的,但在此之前,daymonth 的值分别左移 23 位和 28 位。后面的数字是根据这些成员在位字段中所占的对应位位置来取的。注意,当需要打印输出时,仍可分别访问每个成员。

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>

union {
    struct {
        uint32_t year:23;
        uint32_t day:5;
        uint32_t month:4;
    };
    uint32_t ydm;
} typedef BitfieldFast;

int main() {

    BitfieldFast date_f;
    date_f.ydm = 2020 | (13 << 23) | (12 << 28);

    printf("sizeof BitfieldFast: %lu bytes\n", sizeof(date_f));
    printf("date_f: %d/%d/%d \n", date_f.day, date_f.month, date_f.year);

    return EXIT_SUCCESS;
}

输出:

sizeof BitfieldFast: 4 bytes
date_f: 13/12/2020

位掩码的另一个典型使用例子是网络中的 IP 地址。也就是说,IP 地址提供了网络掩码,它决定了给定地址属于哪个网络。网络地址的计算是通过对 IP 地址和它的网络掩码进行逻辑运算来完成的。在这种情况下,我们定义了位字段 struct 来分别存储 IP 地址和网络掩码。请注意,逻辑与运算是在整个 32 位的值上完成的,但是当我们把地址打印成 4 个 8 位的部分时,使用的是成员访问运算符。

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>

union {
    struct {
        uint8_t first:8;
        uint8_t second:8;
        uint8_t third:8;
        uint8_t fourth:8;
    };
    uint32_t ip;
} typedef IPAddress;

int main() {

    IPAddress ip1 = {10,127,5,1};
    IPAddress mask = {255,255,240,0};
    printf("ip1: %d.%d.%d.%d\n", ip1.first, ip1.second, ip1.third, ip1.fourth);
    ip1.ip = ip1.ip & mask.ip;
    printf("net: %d.%d.%d.%d\n", ip1.first, ip1.second, ip1.third, ip1.fourth);

    return EXIT_SUCCESS;
}

输出:

ip1: 10.127.5.1
net: 10.127.0.0
Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn