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