Bài 31.324 phút đọc
Bật, tắt, đảo và kiểm tra bit
Sau bài này bạn sẽ làm được
- Bật, tắt, đảo và kiểm tra một bit bằng biểu thức đúng
- Viết bộ macro thao tác bit an toàn
- Nhận ra lỗi tràn khi dịch quá ba mươi mốt bit
- Đọc và ghi một trường nhiều bit liền nhau
Bốn thao tác: bật, tắt, đảo, kiểm tra. Mỗi cái một biểu thức, và bốn biểu thức đó xuất hiện trong mọi trình điều khiển thiết bị từng được viết.
#Bốn thao tác cơ bản
| Thao tác | Biểu thức | Vì sao nó chạy |
|---|---|---|
| Bật bit n | x |= 1u << n | OR với 1 luôn cho 1, OR với 0 giữ nguyên |
| Tắt bit n | x &= ~(1u << n) | AND với 0 luôn cho 0, AND với 1 giữ nguyên |
| Đảo bit n | x ^= 1u << n | XOR với 1 đảo, XOR với 0 giữ nguyên |
| Kiểm bit n | (x >> n) & 1u | Kéo bit về vị trí 0 rồi che mọi bit khác |
terminal
./bit-thao-tac
x 10110001 = 0xB1 = 177 BIT(2) 00000100 = 0x04 = 4 x |= BIT(2) 10110101 = 0xB5 = 181 x &= ~BIT(2) 10110001 = 0xB1 = 177 x ^= BIT(2) 10110101 = 0xB5 = 181 KIEM_BIT(x, 2) = 0, KIEM_BIT(x, 4) = 1
#Bộ macro nên có
bit.h
#ifndef BIT_H
#define BIT_H
#include <stdint.h>
/* Mat na chi co bit n bang 1 */
#define BIT(n) (1u << (n))
#define BIT64(n) (1ULL << (n))
/* Bon thao tac co ban */
#define BAT_BIT(x, n) ((x) |= BIT(n))
#define TAT_BIT(x, n) ((x) &= ~BIT(n))
#define DAO_BIT(x, n) ((x) ^= BIT(n))
#define KIEM_BIT(x, n) (((x) >> (n)) & 1u)
/* Mat na cho khoang bit tu lo den hi, ca hai dau mut */
#define MAT_NA(hi, lo) ((~0u >> (31 - (hi))) & (~0u << (lo)))
/* Doc va ghi mot truong nhieu bit */
#define DOC_TRUONG(x, hi, lo) (((x) & MAT_NA(hi, lo)) >> (lo))
#define GHI_TRUONG(x, hi, lo, v) \
((x) = ((x) & ~MAT_NA(hi, lo)) \
| (((unsigned)(v) << (lo)) & MAT_NA(hi, lo)))
#endif /* BIT_H */#Ghi một giá trị đúng sai vào bit
/* Cach 1: re nhanh */
#define GHI_BIT(x, n, v) ((v) ? BAT_BIT(x, n) : TAT_BIT(x, n))
/* Cach 2: khong re nhanh */
x = (x & ~BIT(n)) | ((unsigned)!!(v) << (n));terminal
./ghi-bit
ban an toan: x = 8 (mong doi 8) sau khi ghi 0: x = 0 (mong doi 0)
#Thao tác nhiều bit cùng lúc
/* Mat na co the co nhieu bit, va bon phep toan van dung nguyen */
#define CO_DOC BIT(0)
#define CO_GHI BIT(1)
#define CO_THUC_THI BIT(2)
#define CO_AN BIT(3)
unsigned quyen = 0;
quyen |= CO_DOC | CO_GHI; /* bat HAI co mot luc */
quyen &= ~(CO_GHI | CO_AN); /* tat HAI co mot luc */
quyen ^= CO_THUC_THI; /* dao mot co */
if (quyen & CO_DOC) /* co it nhat CO_DOC */
if ((quyen & (CO_DOC | CO_GHI)) == (CO_DOC | CO_GHI)) /* co CA HAI */
if (quyen & (CO_DOC | CO_GHI)) /* co it nhat MOT trong hai */Đọc và ghi một trường nhiều bit liền nhau
/* Thanh ghi cau hinh 8 bit:
bit 7..4 toc do (0 den 15)
bit 3..2 che do (0 den 3)
bit 1 bat
bit 0 ngat */
unsigned cau_hinh = 0xB6; /* 10110110 */
unsigned toc_do = (cau_hinh & 0xF0) >> 4; /* 1011 = 11 */
unsigned che_do = (cau_hinh & 0x0C) >> 2; /* 01 = 1 */
unsigned bat = (cau_hinh >> 1) & 1u; /* 1 */
unsigned ngat = cau_hinh & 1u; /* 0 */terminal
./truong-bit
y 10110110 = 0xB6 = 182 MAT_NA 11110000 = 0xF0 = 240 y & MAT_NA 10110000 = 0xB0 = 176 roi >> 4 00001011 = 0x0B = 11
#Ba cạm bẫy còn lại
Một: dịch quá số bit của kiểu
uint8_t co = 0;
BAT_BIT(co, 10); /* BIT(10) la 1024, khong nam trong uint8_t.
Phep OR duoc tinh tren int, roi cat con 8 bit,
nen co khong doi. Khong ai canh bao. */
/* Cach phong: kiem luc bien dich khi n la hang so */
#define BAT_BIT_KIEM(x, n) \
do { \
_Static_assert((n) < sizeof(x) * CHAR_BIT, \
"so hieu bit vuot do rong cua bien"); \
(x) |= BIT(n); \
} while (0)
/* Chi chay khi n la hang so bien dich. Voi n la bien thi
phai kiem luc chay: */
if (n < sizeof co * CHAR_BIT) BAT_BIT(co, n);Hai: thăng hạng nguyên với kiểu nhỏ
uint8_t co = 0xFF;
uint8_t dao = ~co; /* ~co tinh tren INT:
co thang hang thanh 255
~255 = -256
gan vao uint8_t -> 0
Ket qua DUNG, nhung chi vi tinh co. */
uint16_t a = 0xFFFF;
uint32_t b = a << 16; /* a thang hang thanh int co dau 32 bit
0xFFFF << 16 khong nam trong int
-> HANH VI KHONG XAC DINH */
uint32_t c = (uint32_t)a << 16; /* dung */
/* Quy tac: voi uint8_t va uint16_t, EP sang unsigned hoac uint32_t
truoc khi dich hoac dao. Bai 31.2 da noi, va no lap lai o day
vi day la cho no hay xay ra nhat. */Ba: quên rằng bit đánh số từ 0
/* Datasheet noi: "bit 5 la co bao san sang".
Do la bit thu SAU tinh tu phai, va so hieu la 5.
Mat na la BIT(5) = 0x20, khong phai BIT(6). */
uint32_t co_san_sang = KIEM_BIT(thanh_ghi, 5);
/* Voi mot truong tu bit 4 den bit 7:
so bit = 7 - 4 + 1 = BON bit
mat na = 0xF0
gia tri = (x & 0xF0) >> 4
Cong thuc so bit la hi - lo + 1, va dau cong 1 rat hay bi quen. */Tự làm thử
- Viết bốn biểu thức bật, tắt, đảo, kiểm và chạy chúng trên
0xB1với bit 2 và bit 4. - Viết
BIT(n)bằng1thay vì1urồi gọiBIT(31), chạy dưới UBSan nếu có. - Viết ba bản
MAT_NAở trên và kiểm cả ba bằng năm trường hợp trong bảng. - Ghi giá trị 0x3 vào trường bit 4 tới 7 của
0xB6và xác nhận kết quả là0x36. - Ghi giá trị 0xFF vào cùng trường đó và xác nhận bit thừa bị che.
- Viết tệp kiểm thử đầy đủ cho
bit.hcủa bạn.
Trình chấm điểm tự động sẽ được bổ sung ở giai đoạn sau. Hiện tại bạn tự chạy thử trên máy.
Tóm tắt
- Bốn thao tác: OR để bật, AND với mặt nạ đảo để tắt, XOR để đảo, dịch rồi AND với 1 để kiểm.
BIT(n)phải dùng1u, vàBIT64(n)phải dùng1ULLcho bit từ 32 trở lên.- Ghi vào một trường nhiều bit cần đủ ba bước: xóa trường cũ, dịch giá trị mới lên vị trí, và che giá trị mới bằng mặt nạ.
- Kiểm nhiều cờ phải phân biệt
x & cannghĩa là bất kỳ với(x & can) == cannghĩa là tất cả. - Với
uint8_tvàuint16_t, ép sanguint32_ttrước khi dịch, vì thăng hạng nguyên đưa chúng vềintcó dấu.