Bài 51.426 phút đọc
Đọc chuẩn và viết mã khả chuyển
Sau bài này bạn sẽ làm được
- Tìm và tra được một mục trong bản nháp chuẩn
- Biết Annex J liệt kê gì
- Kể bảy giả định sai làm mã không khả chuyển
- Kiểm chứng khả chuyển bằng nhiều trình biên dịch
Bản chuẩn chính thức của ISO tốn vài trăm đô la. Bản nháp cuối cùng trước khi xuất bản thì miễn phí và khác bản chính thức đúng vài lỗi chính tả. Đó là tài liệu bạn nên có sẵn.
#Bản nháp miễn phí
| Mã bản nháp | Chuẩn | Ghi chú |
|---|---|---|
| N1256 | C99 | Đã gộp ba bản đính chính |
| N1570 | C11 | Bản hay được trích dẫn nhất, dễ tìm nhất |
| N2176 | C17 | Ít khác N1570 |
| N3220 | C23 | Bản nháp gần cuối |
#Cấu trúc tài liệu
| Mục | Nội dung | Bạn sẽ mở nó khi |
|---|---|---|
| 1 tới 4 | Phạm vi, thuật ngữ, mức tuân thủ | Hiếm |
| 5 | Môi trường: khởi động chương trình, giới hạn dịch | Tìm giới hạn tối thiểu chuẩn bảo đảm |
| 6.2 | Khái niệm: phạm vi, liên kết, thời gian lưu trữ, kiểu | Bài 52.1 và 52.3 dùng mục này |
| 6.3 | Chuyển đổi kiểu | Khi một phép tính cho kết quả lạ |
| 6.5 | Biểu thức, độ ưu tiên, điểm tuần tự | Bài 33.5 và 33.6 dùng mục này |
| 6.7 | Khai báo, bao gồm bố cục struct | Bài 53.1 dùng mục này |
| 6.8 | Câu lệnh | Hiếm |
| 6.10 | Bộ tiền xử lý | Chương 54 dùng mục này |
| 7 | Thư viện chuẩn. 7.21 stdio, 7.22 stdlib, 7.24 string | Rất thường xuyên |
| Phụ lục A | Tóm tắt cú pháp, dạng BNF | Khi viết trình phân tích |
| Phụ lục J | Danh sách đầy đủ UB, unspecified, implementation-defined | Bài 33.1 tới 33.7 dùng phụ lục này |
#Cách đọc một mục
Một mục thật, 6.5.6 đoạn 8, về số học con trỏ
When an expression that has integer type is added to or subtracted
from a pointer, the result has the type of the pointer operand. [...]
if the expression P points to the i-th element of an array object,
the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has
the value n) point to, respectively, the i+n-th and i-n-th elements
of the array object, provided they exist. Moreover, if the expression
P points to the last element of an array object, the expression (P)+1
points one past the last element of the array object, and if the
expression Q points one past the last element of an array object,
the expression (Q)-1 points to the last element of the array object.
If both the pointer operand and the result point one past the last
element of the array object, the evaluation shall not produce an
overflow; otherwise, the behavior is undefined.#Bảy giả định sai
Bảy giả định hay gặp
sizeof(int) == 4 /* co the la 2 tren vi dieu khien */
sizeof(long) == 8 /* la 4 tren Windows 64 bit */
char co dau /* khong dau tren ARM32 va PowerPC */
CHAR_BIT == 8 /* gan nhu luon dung, nhung khong chuan */
nho truoc, little endian /* mang va mot so CPU la lon truoc */
NULL la toan bit 0 /* bieu dien khong duoc bao dam */
con tro va so nguyen cung do rong /* khong bao dam */Cách viết an toàn
#include <stdint.h>
#include <limits.h>
int32_t x; /* dung n bit o moi may */
size_t n; /* cho kich thuoc va chi so */
uintptr_t p_so; /* du chua con tro */
unsigned char b; /* cho du lieu byte */
_Static_assert(CHAR_BIT == 8, "can byte 8 bit");
/* Doc va ghi so nhi phan bang dich bit, Bai 33.7: */
uint32_t doc_u32(const unsigned char *t) {
return (uint32_t)t[0] << 24 | (uint32_t)t[1] << 16
| (uint32_t)t[2] << 8 | (uint32_t)t[3];
}
/* Va gan NULL bang cach viet NULL, khong bang memset ve 0: */
p = NULL; /* dung */
memset(&p, 0, sizeof p); /* KHONG bao dam cho ra con tro NULL */terminal
# Đo trên MinGW-W64, x86-64, Windows
./impl
sizeof(int) = 4 sizeof(long) = 4 sizeof(long long) = 8 sizeof(void *) = 8 CHAR_BIT = 8 CHAR_MIN = -128 -> char CO dau
#Kiểm chứng khả chuyển
Sáu cấu hình đáng dịch thử
# 1. Chuan nghiem ngat
gcc -std=c17 -pedantic-errors -Wall -Wextra prog.c
# 2. 32 bit, bat gia dinh ve kich thuoc con tro
gcc -m32 -std=c17 prog.c
# 3. Trinh bien dich khac, bat canh bao khac
clang -std=c17 -Wall -Wextra prog.c
clang -Weverything prog.c # BAT MOI canh bao, rat nhieu bao gia
# 4. Nen tang khac
x86_64-w64-mingw32-gcc prog.c # Windows tu Linux
arm-none-eabi-gcc prog.c # ARM, char khong dau
# 5. char khong dau, gia lap ARM tren x86
gcc -funsigned-char prog.c
# 6. Sanitizer, bat gia dinh sai luc chay
gcc -fsanitize=undefined,address prog.cMa trận kiểm tra trong tích hợp liên tục
# .github/workflows/kiem.yml, dai y
chien_luoc:
ma_tran:
trinh_bien_dich: [gcc, clang]
chuan: [c99, c11, c17]
do_rong: [32, 64]
he: [ubuntu, macos, windows]
buoc:
- chay: |
$CC -std=$CHUAN -m$DO_RONG \
-pedantic-errors -Wall -Wextra -Werror \
-fsanitize=undefined,address \
-o thu *.c
./thu
# 2 x 3 x 2 x 3 = 36 to hop. Nhieu, nhung moi to hop chay vai giay
# va chung chay song song.
#
# Trong thuc te, bon to hop dau tien bat gan het van de:
# gcc c17 64 ubuntu
# clang c17 64 macos
# gcc c99 32 ubuntu
# mingw c17 64 windowsTự làm thử
- Tải N1570 và tìm mục nói về số học con trỏ.
- Đọc hết phụ lục J.2 và đánh dấu năm mục bạn từng vi phạm.
- Tra trong chuẩn xem
fopentrả về gì khi thất bại. - Dịch một dự án với
-funsigned-charvà đếm số lỗi mới. - Chạy
clang -Weverythingvà chọn ra năm cảnh báo đáng bật thường xuyên. - Viết ma trận kiểm tra bốn cấu hình cho một dự án củ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 nháp N1570 cho C11 miễn phí và khác bản chính thức không đáng kể.
- Mục 6 là ngôn ngữ, mục 7 là thư viện, và phụ lục J liệt kê toàn bộ hành vi không xác định.
shalllà bắt buộc,maylà được phép, và chú thích ở chân trang không có hiệu lực nhưng rất hữu ích để hiểu.- Chuẩn bảo đảm phạm vi tối thiểu chứ không bảo đảm kích thước, và
sizeof(char)bằng 1 theo định nghĩa. -funsigned-charlà cách rẻ nhất để giả lập ARM trên x86 và bắt lỗi phụ thuộc vào dấu củachar.