Bài 33.724 phút đọc
Implementation-defined và unspecified
Sau bài này bạn sẽ làm được
- Phân biệt implementation-defined với unspecified bằng ví dụ
- Kể năm thứ implementation-defined hay gây lỗi khi chuyển máy
- Tra tài liệu trình biên dịch để biết lựa chọn của nó
- Viết mã không phụ thuộc vào những lựa chọn đó
Đây là nhóm hành vi hiền lành nhất trong ba nhóm: chương trình vẫn có nghĩa, chỉ là kết quả khác nhau giữa các máy. Nhưng chính vì hiền lành mà nó sống sót qua mọi bài kiểm thử, rồi lộ ra đúng lúc bạn chuyển sang nền tảng khác.
#Phân biệt bằng hai câu hỏi
| Implementation-defined | Unspecified | |
|---|---|---|
| Trình biên dịch phải ghi vào tài liệu | Có | Không |
| Phải nhất quán trong cùng một chương trình | Có | Không |
| Tra cứu trước được | Có | Không |
| Chương trình vẫn hợp lệ | Có | Có |
| Ví dụ | sizeof(int), char có dấu | Thứ tự đánh giá đối số |
#Kích thước kiểu
terminal
# MinGW-W64, x86-64, Windows 11
gcc -std=c11 -O2 -o impl impl.c && ./impl
sizeof(char) = 1 sizeof(short) = 2 sizeof(int) = 4 sizeof(long) = 4 sizeof(long long) = 8 sizeof(void *) = 8 CHAR_BIT = 8
| Kiểu | Windows 64 bit | Linux 64 bit | Vi điều khiển 16 bit | Chuẩn bảo đảm |
|---|---|---|---|---|
| char | 1 | 1 | 1 | Đúng 1, theo định nghĩa |
| short | 2 | 2 | 2 | Ít nhất 2 |
| int | 4 | 4 | 2 | Ít nhất 2 |
| long | 4 | 8 | 4 | Ít nhất 4 |
| long long | 8 | 8 | 8 | Ít nhất 8 |
| void * | 8 | 8 | 2 hoặc 4 | Không bảo đảm gì |
| size_t | 8 | 8 | 2 hoặc 4 | Đủ chứa sizeof lớn nhất |
#char có dấu hay không
terminal
./impl
CHAR_MIN = -128 -> char CO dau char c = 200 -> -56
Dùng char trần cho dữ liệu byte
char c = doc_byte();
if (c == 0xFF) { } /* KHONG BAO GIO dung khi char co dau:
0xFF la 255, con c toi da la 127 */
if (c > 127) { } /* luon sai khi char co dau */
int n = c; /* 0xFF thanh -1 hay 255? Tuy may */
int chi_so = ten[i]; /* neu ten[i] am thi truy cap ngoai bien */
bang[chi_so]++;unsigned char cho dữ liệu
unsigned char c = doc_byte();
if (c == 0xFF) { } /* dung */
int n = c; /* luon 0..255 */
int chi_so = (unsigned char)ten[i]; /* luon khong am */
bang[chi_so]++;
/* Quy tac:
char -> KY TU van ban
unsigned char -> BYTE du lieu tho
signed char -> so nho co dau, hiem khi can */#Thứ tự byte
Kiểm tra thứ tự byte lúc chạy
#include <stdio.h>
#include <stdint.h>
#include <string.h>
int main(void) {
uint32_t x = 0x01020304;
unsigned char b[4];
memcpy(b, &x, sizeof b);
printf("0x01020304 trong bo nho: %02X %02X %02X %02X\n",
b[0], b[1], b[2], b[3]);
printf("May nay: %s\n", b[0] == 0x04 ? "nho truoc" : "lon truoc");
return 0;
}terminal
gcc -std=c11 -O2 -o bo bo.c && ./bo
0x01020304 trong bo nho: 04 03 02 01 May nay: nho truoc
#Bốn thứ còn lại
Dịch phải số âm
terminal
./impl
-8 >> 1 = -4 (dich so hoc)
int am = -8;
am >> 1; /* -4 tren may nay: dich SO HOC, giu dau.
Chuan noi day la implementation-defined: cai dat duoc chon
dich so hoc hoac dich logic.
Thuc te moi trinh bien dich hien dai deu dich so hoc, va C23
da chuan hoa luon. Nhung neu ban viet ma cho C11 tro xuong
thi van nen tranh. */
/* Cach chac chan: chuyen sang khong dau truoc. */
unsigned u = (unsigned)am;
u >> 1; /* luon dich logic */Chia số âm
terminal
./impl
-7 / 2 = -3 -7 % 2 = -1
-7 / 2 /* C99 tro len: -3, cat VE PHIA KHONG.
Truoc C99: implementation-defined, co the -4 */
-7 % 2 /* -1. Dau cua phan du theo dau cua SO BI CHIA */
/* Quy tac C99: (a / b) * b + a % b == a luon dung.
Chu y day KHAC voi Python, noi -7 // 2 la -4 va -7 % 2 la 1.
Neu ban chuyen thuat toan tu Python sang C thi phai kiem lai. */
/* Muon chia lam tron xuong nhu Python: */
int chia_xuong(int a, int b) {
int q = a / b;
if ((a % b != 0) && ((a < 0) != (b < 0))) --q;
return q;
}Trường bit
struct {
unsigned a : 3;
unsigned b : 5;
} s;
/* Implementation-defined:
- a nam o bit thap hay bit cao cua byte dau
- co byte dem giua a va b khong
- "unsigned x : 3" hop le, nhung "int x : 3" co dau hay khong
Nghia la KHONG duoc dung truong bit de anh xa dinh dang tep
hay thanh ghi phan cung. Dung mat na va dich bit, Bai 31.4. */Kiểu cơ sở của enum
enum Mau { DO, XANH, VANG };
sizeof(enum Mau) /* implementation-defined: thuong la 4,
nhung trinh bien dich duoc chon kieu
nho nhat du chua */
/* Nen khong ghi enum thang ra tep, va khong dua enum qua
ranh gioi ABI ma khong co dinh kieu.
C23 cho phep chi dinh: */
enum Mau : uint8_t { DO, XANH, VANG };#Viết mã không phụ thuộc
Kiểm tra giả định ngay lúc dịch
#include <assert.h>
#include <limits.h>
#include <stdint.h>
_Static_assert(CHAR_BIT == 8,
"ma nay gia dinh byte 8 bit");
_Static_assert(sizeof(int) >= 4,
"ma nay gia dinh int it nhat 32 bit");
_Static_assert(sizeof(void *) == sizeof(uintptr_t),
"uintptr_t phai du chua con tro");
_Static_assert(sizeof(SinhVien) == 112,
"kich thuoc ban ghi doi, phai tang phien ban dinh dang tep");
/* Neu mot gia dinh sai thi chuong trinh KHONG DICH DUOC, kem thong bao
ban tu viet. Tot hon nhieu so voi viec no dich duoc roi chay sai
tren may khach.
Bai 19.7 da gioi thieu _Static_assert. */terminal
# Cố tình cho sai một giả định để xem thông báo
gcc -std=c11 -c gd.c
gd.c:10:1: error: static assertion failed: "ma nay gia dinh int it nhat 3200 bit" _Static_assert(sizeof(int) >= 400, ^~~~~~~~~~~~~~
Thông báo là câu bạn tự viết, chứ không phải một thông báo chung chung của trình biên dịch. Người gặp lỗi đọc là hiểu ngay phải làm gì.
Tự làm thử
- Chạy chương trình in
sizeofmọi kiểu trên hai hệ điều hành khác nhau. - Viết chương trình xác định thứ tự byte của máy bạn.
- Chứng minh
char c = 200in ra-56và giải thích vì sao. - Viết cặp hàm
ghi_u32vàdoc_u32rồi kiểm tra chúng đảo ngược nhau. - Thêm bốn dòng
_Static_assertvào một dự án của bạn và thử phá vỡ một giả định. - Dịch một dự án cũ với
-m32và đếm số lỗi mới.
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
- Implementation-defined thì tra cứu được và nhất quán, unspecified thì không. Cả hai đều không phải UB.
sizeof(long)là 4 trên Windows và 8 trên Linux, nên ép con trỏ vàolonglà mã hỏng.charlà kiểu thứ ba, khác cảsigned charlẫnunsigned char, và dấu của nó tùy nền tảng.- Mọi hàm
ctype.hphải nhận đối số đã ép sangunsigned char, nếu không là UB. _Static_assertbiến một giả định ngầm thành lỗi biên dịch có thông báo do bạn viết.