給定一個 16 位整數(`int16_t` / `uint16_t`),需提取其高 8 位與低 8 位,分別存入兩個 8 位變量(`uint8_t`)。
#include
#include
void split_u16(uint16_t num, uint8_t *high, uint8_t *low) {
*high = (uint8_t)(num >> 8); // 邏輯右移 8 位,取高位字節
*low = (uint8_t)(num & 0xFF); // 掩碼取低位字節(等價於 (uint8_t)num)
}
int main() {
uint16_t val = 0x1234; // 4660
uint8_t hi, lo;
split_u16(val, &hi, &lo);
printf("0x%04X -> High: 0x%02X, Low: 0x%02X\n", val, hi, lo);
// 輸出: 0x1234 -> High: 0x12, Low: 0x34
return 0;
}
若輸入為有符號 16 位整數,需先轉為無符號再操作,避免符號擴展污染高位:
void split_i16(int16_t num, uint8_t *high, uint8_t *low) {
uint16_t uval = (uint16_t)num; // 關鍵:重新解釋位模式為無符號
*high = (uint8_t)(uval >> 8);
*low = (uint8_t)(uval & 0xFF);
}
/* 測試負數 */
int16_t neg = -1; // 0xFFFF (二進制補碼)
uint8_t hi, lo;
split_i16(neg, &hi, &lo);
// 結果: High: 0xFF, Low: 0xFF (正確提取位模式)
上述位運算與機器大小端無關,操作的是值的位模式。若需按記憶體順序存取(如網路傳輸/檔案存儲),需顯式處理大小端:
/* 網路序 (大端) 寫入緩衝區 */
void write_u16_be(uint8_t *buf, uint16_t val) {
buf[0] = (uint8_t)(val >> 8); // 高位在前
buf[1] = (uint8_t)(val & 0xFF);
}
/* 主機序讀取 (假設小端機器) */
uint16_t read_u16_le(const uint8_t *buf) {
return (uint16_t)buf[0] | ((uint16_t)buf[1] << 8);
}
| 錯誤代碼 | 問題 | 修正 |
|---|---|---|
| `high = (num >> 8) & 0xFF;` | 多餘掩碼,`>>8` 已移除低位 | `high = num >> 8;` 即可 |
| `low = num & 0xFF00;` | 掩碼錯誤,取到高位 | `low = num & 0x00FF;` 或 `low = (uint8_t)num;` |
| 對 `int16_t` 直接 `>> 8` | 符號擴展導致高位全 1 (負數) | 先轉 `uint16_t` 再移位 |
| 用聯合體 `union { uint16_t u16; uint8_t u8[2]; }` | 違反嚴格別名規則,C 標準未定義行為 | 用位運算或 `memcpy` (C11 保證安全) |
掌握位運算是嵌入式、系統程式、協議解析、高效能運算的基本功,請熟練掌握 `&` `|` `^` `~` `<<` `>>` 及配合 `