C 语言中的位掩码

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

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

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

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

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

#include <stdint.h>
#include <stdio.h>
#include <stdlib.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 <stdint.h>
#include <stdio.h>
#include <stdlib.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 <stdint.h>
#include <stdio.h>
#include <stdlib.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
作者: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

DelftStack.com 创始人。Jinku 在机器人和汽车行业工作了8多年。他在自动测试、远程测试及从耐久性测试中创建报告时磨练了自己的编程技能。他拥有电气/电子工程背景,但他也扩展了自己的兴趣到嵌入式电子、嵌入式编程以及前端和后端编程。

LinkedIn Facebook