C 语言中的位掩码
Jinku Hu
2023年10月12日
本文将演示关于如何在 C 语言中使用位掩码的多种方法。
使用 struct
关键字在 C 语言中定义位掩码数据
位掩码通常用于位性操作,以访问或设置位场式数据结构的各个部分。另一方面,利用位域来有效地存储数据,减少内存占用。同时,位智运算在硬件上的执行速度相对比普通的算术运算要快。在下面的例子中,我们使用 struct
关键字来演示位域的实现。
请注意,这是一种特殊的符号,用来构造对象,在这个对象中,可以使用传统的成员访问操作符来检索给定的位域。Bitfield
结构在内存中存储了一个占用 32 位的无符号整数,但它也可以以 3 个不同大小的部分–23 位、5 位和 4 位的值来访问,分别命名为 year
、day
和 month
。因此,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
整数成员。
请注意,代表给定日期的十进制数在逻辑上是相互或
的,但在此之前,day
和 month
的值分别左移 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