Bài 31.122 phút đọc
Biểu diễn nhị phân
Sau bài này bạn sẽ làm được
- Viết hằng số dạng nhị phân, thập lục phân và bát phân
- Nhận ra cái bẫy số 0 ở đầu biến hằng thành bát phân
- Viết hàm in bit của một vùng nhớ bất kỳ
- Đọc biểu diễn bit của số nguyên, số thực và con trỏ
Trước khi thao tác bit, phải nhìn thấy chúng. Bài này viết một hàm in biểu diễn nhị phân của bất kỳ đối tượng nào, và dùng nó để nhìn vào số nguyên, số thực, ký tự và con trỏ.
#Bốn cách viết hằng số
int a = 42; /* thap phan */
int b = 0x2A; /* thap luc phan, moi chu so bang 4 bit */
int c = 052; /* BAT PHAN, vi bat dau bang so 0 */
int d = 0b101010; /* nhi phan, C23. GCC ho tro tu lau nhu mo rong */
/* Ca bon deu bang 42. */| Dạng | Tiền tố | Cơ số | Chuẩn từ | Dùng khi |
|---|---|---|---|---|
| 42 | không có | 10 | C89 | Số lượng, chỉ số, giá trị thường |
| 0x2A | 0x hoặc 0X | 16 | C89 | Mặt nạ bit, địa chỉ, mã byte |
| 052 | 0 | 8 | C89 | Quyền tệp trên Unix, hầu như không dùng gì khác |
| 0b101010 | 0b hoặc 0B | 2 | C23 | Mẫu bit ngắn, khi muốn nhìn thấy từng bit |
#Cái bẫy số 0 ở đầu
Số 0 ở đầu để căn lề
int thang = 08; /* LOI BIEN DICH:
error: invalid digit "8" in octal constant */
int ngay = 07; /* Bien dich duoc, nhung bang 7 tinh co */
int gio = 010; /* Bien dich duoc, va bang TAM, khong phai muoi */
int ma[] = { 001, 002, 010, 011, 012 };
/* Nguoi viet muon: 1, 2, 10, 11, 12
Thuc te nhan: 1, 2, 8, 9, 10 */Không bao giờ viết số 0 ở đầu
int thang = 8;
int ngay = 7;
int gio = 10;
int ma[] = { 1, 2, 10, 11, 12 };
/* Muon can le thi dung khoang trang, khong dung so 0: */
int ma2[] = { 1, 2, 10, 11, 12 };terminal
./bit-demo
42 = 0x2A = 052 052 (bat phan) = 42 0x2A = 42
#Hàm in bit
in-bit.c
#include <stdio.h>
#include <stddef.h>
/* In bieu dien nhi phan cua BAT KY doi tuong nao.
In tu byte co dia chi CAO nhat xuong thap nhat, nen tren may
nho truoc thi ket qua doc nhu mot so viet binh thuong. */
void in_bit(const void *obj, size_t n) {
const unsigned char *p = obj;
for (size_t i = n; i-- > 0; ) {
for (int b = 7; b >= 0; --b)
putchar((p[i] >> b & 1) ? '1' : '0');
putchar(' ');
}
putchar('\n');
}
int main(void) {
int x = 42; printf("int 42 : "); in_bit(&x, sizeof x);
int y = -42; printf("int -42 : "); in_bit(&y, sizeof y);
float f = 1.5f; printf("float 1.5 : "); in_bit(&f, sizeof f);
double d = 1.5; printf("double 1.5 : "); in_bit(&d, sizeof d);
char c = 'A'; printf("char 'A' : "); in_bit(&c, sizeof c);
void *p = &x; printf("con tro : "); in_bit(&p, sizeof p);
return 0;
}terminal
gcc -std=c11 -Wall -Wextra -o in-bit in-bit.c && ./in-bit
int 42 : 00000000 00000000 00000000 00101010 int -42 : 11111111 11111111 11111111 11010110 float 1.5 : 00111111 11000000 00000000 00000000 double 1.5 : 00111111 11111000 00000000 00000000 00000000 00000000 00000000 00000000 char 'A' : 01000001 con tro : 00000000 00000000 00000000 00000000 00000000 01100001 11111110 00111100
#Đọc kết quả
Số nguyên âm: bù hai
int 42 : 00000000 00000000 00000000 00101010
int -42 : 11111111 11111111 11111111 11010110
/* Doi tu 42 sang -42:
1. dao het bit: 11111111 11111111 11111111 11010101
2. cong 1: 11111111 11111111 11111111 11010110
Do la bu hai. Kiem lai:
42 = 00000000 ... 00101010
~42 = 11111111 ... 11010101
~42 + 1 = 11111111 ... 11010110 dung */
/* Tu C23, bu hai la bat buoc. Truoc do chuan cho phep ca
bu mot va dau va do lon, nhung khong may nao con dung. */Số thực: dấu, số mũ, định trị
float 1.5 : 0 01111111 10000000000000000000000
^ ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
| | dinh tri, 23 bit
| so mu, 8 bit
dau, 1 bit
/* Giai ma:
dau = 0 -> so duong
so mu = 01111111 = 127, tru do lech 127 -> 0
dinh tri= 1000... -> 1.1 nhi phan = 1.5 thap phan
Gia tri = 1.5 x 2^0 = 1.5 dung
double 1.5 : 0 01111111111 1000000000...
^ ^^^^^^^^^^^ ^^^^^^^^^^^^
| | dinh tri, 52 bit
| so mu, 11 bit, do lech 1023
dau
Bai 3.4 da noi ve IEEE 754. Bay gio ban nhin thay no. */Con trỏ
con tro : 00000000 00000000 00000000 00000000 00000000 01100001 11111110 00111100
/* Tam byte, nhung muoi sau bit cao deu bang 0. Tren x86-64,
khong gian dia chi hien dung 48 bit, phan con lai phai la
ban sao cua bit 47. Do la ly do dia chi tren Linux va Windows
luon co dang 0x00007fff... hoac 0xffff8000...
Vai he thong loi dung 16 bit thua do de nhet co vao con tro,
goi la con tro co the. Ky thuat do rat nhanh va rat nguy hiem,
va no hong khi Intel bat che do 5 tang bang trang. */Tự làm thử
- Viết
in_bitvà chạy nó vớiint,float,doublevà một con trỏ. - In
42và-42rồi kiểm tra quan hệ bù hai bằng tay. - In
0.1,0.5và0.25dạngdouble, chỉ ra cái nào biểu diễn chính xác được. - Viết
int a = 010;rồi ina, giải thích kết quả. - Viết chương trình kiểm tra thứ tự byte của máy bạn.
- Sửa
in_bitđể nó in theo thứ tự byte thật trong bộ nhớ, và so hai bản với cùng một số nguyê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 dạng hằng số: thập phân, thập lục phân với
0x, bát phân với số 0 ở đầu, và nhị phân với0btừ C23. - Số 0 ở đầu biến hằng số thành bát phân, và trình biên dịch chỉ báo lỗi khi có chữ số 8 hoặc 9.
- Mỗi chữ số thập lục phân là đúng bốn bit, nên hex là dạng tự nhiên để viết mặt nạ.
- Hàm in bit chạy qua
const unsigned char *, kiểu duy nhất chuẩn cho phép nhìn mọi đối tượng như dãy byte. - Nhìn bit của
0.1là thấy ngay vì sao số thực không biểu diễn chính xác được.