首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C 語言內嵌組語:單位元組指定位元讀取函式實作詳解

     編輯:關於C語言

應用場景

單色位圖(1 bit per pixel)、硬體暫存器位欄位、通訊協議位流解析等,需從一個 uint8_t 讀取第 pos 位(0~7)的值(0 或 1)。

原始函式碼分析

int byte8_2_int(char a, char pos) {   // a: 目標位元組, pos: 位位置(0-7)
    char reti = 0;
    __asm {
        mov  al, a        ; 載入位元組到 AL
        mov  cl, pos      ; CL = 位位置
        shr  al, cl       ; 邏輯右移 CL 位,目標位移至 LSB
        and  al, 0x01     ; 只保留 LSB
        cmp  al, 0x00
        jnz  set1         ; 若為 1 跳轉
        jmp  A_Exit       ; 為 0 直接返回 reti(=0)
    set1:
        xor  ax, ax       ; 清零 AX
        mov  al, 0x01     ; AL = 1
        mov  reti, al     ; 存回返回值
    }
A_Exit:
    return reti;
}

指令逐行解析

指令作用旗標影響
mov al, a將參數 a 載入 8 位暫存器 AL無
mov cl, pos將 pos 載入 CL(x86 移位指令隱含使用 CL 作為計數)無
shr al, cl邏輯右移 AL,高位補 0,目標位移至第 0 位CF=移出位,ZF/SF/PF 按結果更新
and al, 0x01只保留第 0 位,AL 變 0 或 1ZF=1 若結果 0,CF=0
cmp al, 0比較 AL 與 0,實際上 and 已設 ZF,此指令冗餘同 and
jnz set1ZF=0(結果非零)跳轉-
jmp A_Exit無條件跳轉結束-
set1: xor ax,ax清零 AX(含 AH/AL)ZF=1
mov al, 1設 AL=1ZF=0
mov reti, al存回 C 變數-

優化與可移植改寫

1. 純 C 版本(編譯器自動生成同等/更好代碼)

static inline int byte8_2_int_c(uint8_t byte, unsigned pos) {
    return (byte >> pos) & 1u;
}
  • 無分支、單指令(shr + and 或 bt + setc)。
  • 可移植至 ARM、RISC-V、x64 等所有架構。
  • 加上 static inline 消除呼叫開銷。

2. 內建函式

// GCC/Clang
static inline int byte8_2_int_builtin(uint8_t byte, unsigned pos) {
    return __builtin_bit_test((unsigned long long)byte, pos);
}

// MSVC
#include <intrin.h>
#pragma intrinsic(_bittest)
static inline int byte8_2_int_msvc(uint8_t byte, unsigned pos) {
    return _bittest((long *)&byte, pos);
}

3. 位元組陣列批次讀取(SIMD 加速)

// 讀取 64 個位元組的第 3 位,生成 64 個 0/1 位元組
void extract_bit3_avx2(const uint8_t *src, uint8_t *dst, size_t n) {
    __m256i mask = _mm256_set1_epi8(1 << 3);
    for (size_t i = 0; i < n; i += 32) {
        __m256i v = _mm256_loadu_si256((const __m256i*)(src + i));
        __m256i res = _mm256_and_si256(v, mask);
        res = _mm256_cmpeq_epi8(res, mask);  // 0xFF/0x00
        res = _mm256_and_si256(res, _mm256_set1_epi8(1)); // 1/0
        _mm256_storeu_si256((__m256i*)(dst + i), res);
    }
}

常見錯誤與避坑

  1. 參數型別 char 帶號問題:shr 對帶號 char 仍做邏輯右移(x86 無算術右移 8 位指令),但 C 語言 char 正負實作定義,建議統一用 uint8_t。
  2. pos 範圍未檢查:若 pos >= 8,x86 shr al, cl 只取 CL 低 5 位(模 32),行為與預期不符。應加 assert(pos < 8) 或 pos &= 7。
  3. 內嵌組語語法差異:MSVC 用 __asm { ... },GCC/Clang 用 __asm__ volatile("..." : : : ),跨平台專案避免直接寫組語。
  4. 返回值型別:原函式回傳 int 但實際只用 0/1,建議改 bool 或 uint8_t 明確語意。

測試驗證

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

int main(void) {
    uint8_t test = 0b10101100;  // 0xAC
    for (int p = 0; p < 8; ++p) {
        int expected = (test >> p) & 1;
        int asm_result = byte8_2_int(test, p);
        int c_result = byte8_2_int_c(test, p);
        printf("bit %d: asm=%d c=%d exp=%d %s\n",
               p, asm_result, c_result, expected,
               (asm_result == expected && c_result == expected) ? "OK" : "FAIL");
        assert(asm_result == expected);
        assert(c_result == expected);
    }
    puts("All tests passed.");
    return 0;
}

總結

雖然內嵌組語能精確控制指令流,但現代編譯器對位元操作優化極其成熟。優先使用標準 C 位運算(>>, &)配合 static inline,兼得可移植性、可讀性與極致效能。僅在編譯器無法生成特定指令(如 BIT 測試、CLZ、CRC 硬體加速)時,才以內建函式或內嵌組語補足。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved