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