Đề xuất C++: byte phải chính xác là 8 bit
(open-std.org)- P3477R0 là đề xuất thay đổi tiêu chuẩn nhằm không còn để kích thước byte trong C++ phụ thuộc vào giá trị do triển khai định nghĩa là
CHAR_BIT, mà cố định thành chính xác 8 bit - Các nền tảng hiện đại trên thực tế đã hội tụ quanh giả định byte 8 bit, và GCC, LLVM, MSVC cũng đặt giá trị mặc định hoặc macro liên quan là 8
- POSIX từ POSIX.1-2001 đã yêu cầu
CHAR_BIT == 8, và xu hướng chấp nhận biểu diễn số nguyên bù hai trong C++20 và C23 cũng đi theo cùng hướng - Việc hỗ trợ byte không phải 8 bit để lại những ngoại lệ nhỏ trên toàn bộ ngôn ngữ, thư viện và toolchain, đồng thời tạo ra gánh nặng trường hợp biên không phù hợp với cách dùng C++ hiện đại trong thực tế
- Vẫn có các kiến trúc ngoại lệ như PDP-10 hay một số DSP, nhưng điểm tranh luận cốt lõi là liệu tiêu chuẩn C++ mới có nên tiếp tục duy trì sự phức tạp chỉ vì các mục tiêu đó hay không
Mục tiêu thay đổi của P3477R0
- C++ kế thừa macro
CHAR_BITtừ C, và hiện tại giá trị này là một giá trị do triển khai định nghĩa biểu thị số bit trong một byte - P3477R0 đề xuất sửa tiêu chuẩn C++ để chính thức yêu cầu byte phải là 8 bit
- Trong giai đoạn đầu của ngành điện toán, sự linh hoạt cho phép nhiều kích thước byte khác nhau từng có ý nghĩa, nhưng nhận định ở đây là phần cứng hiện đại gần như đã hội tụ hoàn toàn về giả định byte 8 bit
Tình trạng hiện tại của compiler và nền tảng
- Các compiler lớn trên thực tế đã coi byte 8 bit là mặc định
- Trong các trường hợp hỗ trợ trước đây của GCC,
dsp16xxđã bị loại bỏ vào năm 2004, còn1750abị loại bỏ vào năm 2002 - Khi tìm kiếm trên web có thể thấy một số port ngoài GCC mà
BITS_PER_UNITkhông phải 8, nhưng đề xuất cho rằng chúng dường như không còn liên quan tới C++ hiện đại
Xu hướng từ POSIX và biểu diễn số nguyên
- POSIX từ POSIX.1-2001 đã yêu cầu các điều kiện sau
- byte phải chính xác 8 bit
CHAR_BITphải là8SCHAR_MAXlà127,SCHAR_MINlà-128,UCHAR_MAXlà255
- POSIX giải thích rằng việc bổ sung
int8_tkéo theo yêu cầuchar8 bit và số học bù hai - Sau P0907r4, C++20 chỉ còn hỗ trợ cách lưu trữ bù hai, và C23 cũng đi theo hướng tương tự
- Các hệ điều hành hiện được nêu làm ví dụ tuân thủ POSIX gồm AIX, HP-UX, INTEGRITY, macOS, OpenServer, UnixWare, VxWorks, vz/OS
Chi phí do byte không phải 8 bit để lại
- Phần mềm dành cho byte 8 bit và phần mềm dành cho byte không phải 8 bit không tương thích với nhau; mã C/C++ cho các mục tiêu không dùng byte 8 bit trên thực tế gần như là một phương ngữ không tương thích của C và C++
- Hỗ trợ kiến trúc byte không phải 8 bit để lại sự phức tạp nhỏ nhưng không cần thiết ở nhiều phần của ngôn ngữ và thư viện
- Compiler và toolchain phải tiếp tục gánh các trường hợp biên không phản ánh cách sử dụng hiện đại
- Lập trình viên mới rất dễ bị bối rối bởi những đặc tính mang tính ngoại lai như vậy của C++
- Một số lập trình viên có kinh nghiệm bị cho là phải tốn thời gian cho “tính di động” nhằm phục vụ những nền tảng gần như không còn tồn tại
Các kiến trúc ngoại lệ và phương án dung hòa
- Đề xuất cũng thừa nhận rằng các bộ xử lý không dùng byte 8 bit vẫn còn tồn tại
- Câu hỏi cốt lõi là liệu các bộ xử lý đó có còn liên quan đến C++ hiện đại hay không, và liệu người dùng các bộ xử lý đó có thực sự dùng các phiên bản C++ mới hay không
- Một phương án dung hòa được nêu ra là yêu cầu
CHAR_BIT % 8 == 0, nhưng điều này chỉ có ý nghĩa nếu ủy ban muốn tiếp tục hỗ trợ các DSP hoặc bộ xử lý khác màCHAR_BITkhông phải 8 nhưng là bội số của 8 - PDP-10 được nêu là đối tượng cần bàn, trong khi PDP-11 được phân biệt rõ là dùng byte 8 bit
- Một số DSP xử lý word 24 bit hoặc 32 bit như thể đó là “byte”, và các kiến trúc như vậy từng hợp lý trong thời kỳ kích thước word rất đa dạng và khái niệm byte chưa được chuẩn hóa
Hướng sửa đổi câu chữ trong tiêu chuẩn
- Có ý định sửa định nghĩa byte trong
intro.memoryđể nêu rõ byte, đơn vị lưu trữ cơ bản của mô hình bộ nhớ C++, là 8 bit - Trong
climits, đề xuất thay đổi câu chữ theo hướng đặtCHAR_BITlà8 - Trong
cstdint, vì byte là 8 bit nên các kiểu số nguyên có độ rộng xác định nhưint8_t,uint8_tvà các macro liên quan sẽ không còn là tùy chọn nữa - Với các kiểu dùng
_N_, những trường hợpNkhông phải8,16,32,64vẫn tiếp tục là tùy chọn - Đề xuất cũng bao gồm việc loại bỏ 4 điều khoản mandates liên quan tới
CHAR_BIT == 8tronglocalization
Quan hệ với tiêu chuẩn C
- Đề xuất xem xét liệu C++ có còn nên tiếp tục gắn với các kiến trúc byte không phải 8 bit hay không
- Ủy ban C có thể đi tới kết luận khác đối với ngôn ngữ C
- Việc hai ủy ban đi cùng hướng là điều lý tưởng, nhưng đề xuất này đặt vấn đề theo hướng nhóm liên lạc WG14 và SG22 cung cấp thông tin cho WG21
1 bình luận
Các ý kiến trên Hacker News
Trong loạt bài “Chẳng phải ta có thể thừa nhận máy tính thực tế đều hoạt động như vậy sao?” của JF, đã có một phần nói rằng số nguyên có dấu là bù hai: "Signed Integers are Two’s Complement"
Tuy nhiên, nếu vậy thì đoạn mã kinh điển này của nhân Linux có thể trở nên lỗi thời: https://github.com/torvalds/linux/blob/master/include/math-emu/double.h#L29
Khi thực tập năm 1986, tôi viết mã C trên BBN C/70 dùng byte 10 bit; đó là một trải nghiệm kinh khủng, và ngay từ đầu việc một cỗ máy như vậy tồn tại đã là một tai nạn mang tầm vũ trụ theo nghĩa tiêu cực
Nó dùng word 36 bit và byte 7 bit; khi nhét các byte vào word thì vẫn còn dư bit. Thế mà tôi lại được giao đọc băng chứa dữ liệu nhị phân ở định dạng 8 bit, nên đúng là một mớ hỗn độn
Nhưng C 10 bit thì lại là câu chuyện khác
D đã tiến một bước lớn khi quy định: byte là 8 bit, short là 16 bit, int là 32 bit, long là 64 bit, số học là bù hai, số thực dấu phẩy động là dấu phẩy động IEEE
Điều đó tiết kiệm được lượng thời gian khổng lồ từng bị dùng để cố trừu tượng hóa những thứ như vậy rồi rốt cuộc vẫn sai, và khiến hàng triệu người thở phào. Bộ ký tự cũng là Unicode, không phải EBCDIC hay RADIX-50
u8/i8,u16/i16,u32/i32,u64/i64, và số học cũng được chọn một cách tường minhTràn số của
+là hành vi sai nên sẽ dừng trong debug và releasesafe;+%là wrap bù hai,+|là số học bão hòa.@addWithOverflow()trả về một tuple gồm kiểu gốc vàu1, cònstd.math.add()trả lỗi khi tràn.f16,f32,f64,f80,f128cũng lần lượt là các kiểu dấu phẩy động IEEE có độ dài bit tương ứng. Byte dài bao nhiêu không quan trọng; nếu là máy có byte 12 bit thì cứ dùngu12vài12u8,i32tốt hơn nhiều về mọi mặtunsignedthì xử lý sai, nhưng chuẩn hóa số bit của các kiểu nguyên thủy thì làm đúngbyte = 8 bits,short = 16,int = 32,long = 64,float = 32 bit IEEE,double = 64 bit IEEEVẫn còn những người phải làm việc với DSP: https://thephd.dev/conformance-should-mean-something-fputc-and-freestanding#we-cannot-program-on--vibes-
Cá nhân tôi đang đùa vui bằng cách viết tài liệu cho một console giả tưởng 12 bit chưa được triển khai, với khẩu hiệu “nhiều hơn 50% bit trên mỗi byte so với đối thủ!”, và còn nhét vào các phát minh như “UTF-12”
Tôi đã hỏi suốt mấy năm nhưng chưa nhận được câu trả lời tích cực nào; thứ được nhắc đến chỉ là TI, nên tôi đã thêm thông tin vào bản thảo cập nhật: https://isocpp.org/files/papers/D3477R1.html
CHAR_BIT=32trong một phiên bản như C++30 thì tôi cũng sẽ không khóc lóc phản đối đâuTôi tự hỏi liệu C++ có thể loại bỏ hoặc đơn giản hóa thứ gì đó không
Đây là câu hỏi thật lòng và tôi không theo dõi sát lắm. Tôi nghe nói
rand()đã hỏng và không thể sửa được, nhưng lần cuối tôi biết thì nó vẫn còn chưa được lên kế hoạch loại bỏ. Đề xuất này trông như một phép thử rằng “liệu có thể bỏ cả việc hỗ trợ giải pháp cho một vấn đề mà theo nghĩa đen là chẳng ai gặp phải không?”C và C++ hiện đại đã bỏ điều đó và yêu cầu bù 2. Ở đây, khác biệt “as if” thực tế cũng không quan trọng, và có thể áp dụng cùng kiểu cho
CHAR_BIT, nên rõ ràng đã có tiền lệ cho thay đổi như vậyrandvào diện dự kiến loại bỏ, đồng thời cung cấp phương án thay thếNgoài ra còn có
p2809 Trivial infinite loops are not Undefined Behavior,p1152 Deprecating volatile,p0907 Signed Integers are Two's Complement,p2723 Zero-initialize objects of automatic storage duration,p2186 Removing Garbage Collection Support. Vì vậy việc thay đổi là có thểĐặc tả ngoại lệ cũng đã bị loại bỏ, dù có những người muốn khôi phục nó vì ngoại lệ kiểu giá trị.
auto_ptrcũng bị loại bỏ vì thiết kế hỏng. Tuy nhiên, xét về mặt đơn giản hóa thì cũng không tốt hơn mấy, vì vẫn phải biết cách cũKiểu như cần một ký hiệu C++ mới để chỉ byte 8-bit một cách ổn định mà không phá vỡ tương thích. Ví dụ có thể tạo
unsigned byte8,signed byte8bù 2, và cảbyte8với hành vi dấu không được định nghĩa. Rồi đùa rằng hãy thêmunsigned decimal byte8vàsigned decimal byte8giới hạn miền giá trị 0~10, -10~+10 cho kế toán;centimal byte80~100, -100~+100 cho kế toán tính đến cả chi phí từng byte; một kiểu đủ dùng đại khái cho trườngagetrong cơ sở dữ liệu; và tất nhiên cảfloat byte8nữarand()lại hỏng. Nó tạo ra các giá trị trông có vẻ ngẫu nhiên, và đó là mục đích của nóViệc nó không tạo số ngẫu nhiên an toàn về mật mã là đương nhiên, và các hàm tương đương trong ngôn ngữ khác cũng vậy. Nếu cần một số nguyên khá ngẫu nhiên được tính nhanh thì
rand()hoạt động đủ tốtCảm ơn vì đã quan tâm đến đề xuất; dựa trên phản hồi nhận được đến nay, tôi đã tạo bản nháp cập nhật: https://isocpp.org/files/papers/D3477R1.html
Đặc biệt câu “vấn đề không phải là liệu còn kiến trúc nào mà byte không phải 8 bit hay không. Có! Vấn đề là liệu chúng có quan tâm đến C++ hiện đại không, và C++ hiện đại có quan tâm đến chúng không” rất ấn tượng
Tôi có cảm xúc lẫn lộn về đề xuất này. Một mặt, nó rõ ràng là đúng, và không có ứng dụng có ý nghĩa nào cho việc
CHAR_BITkhác 8Mặt khác, nó cũng có cảm giác như đang khuất phục trước một thế giới quan công bằng, rằng thế giới phải hợp lý và có thể suy luận chỉ dựa trên mô hình cá nhân, quá đơn giản hóa về bên trong máy tính. Cách tiếp cận này đưa ta đi khá xa, nhưng cuối cùng là ngõ cụt; đến cuối cùng phải thừa nhận rằng ta chẳng biết gì cả, và điều tốt nhất là một lập luận hình thức rằng chương trình ta xây dựng là đúng, với điều kiện tài liệu là đúng. Đây là một bước nhảy nhận thức lớn, và với cá nhân tôi, càng lâu không bị buộc phải thừa nhận điều đó thì về sau càng khó vượt qua hơn. Dù vậy, có vẻ các dự án điện tử vật lý đang phổ biến trong giới mới học ngày nay, nên tôi mong “hãy đọc tài liệu” sẽ được thay bằng chuẩn mới là “đọc cái datasheet chết tiệt đi”
autoconf, tôi vẫn thấy nó kiểm tra số bit trong một byte và lưu vàoconfig.h. Như thể thật sự có ai đó định hành động dựa trên giá trị đó vậyCần có cách kiểm tra toàn bộ mã cũ trong quá trình biên dịch để xem macro này đã được dùng chưa. Những thay đổi phá vỡ như vậy cũng có nguy cơ làm ngôn ngữ bị phân nhánh. Việc kiểm thử xem codebase hiện có có dùng macro
CHAR_BIThay không, và có thể cập nhật sang compiler mới hay không, cũng không rõ khó đến mức nào. Cũng nảy sinh các câu hỏi như thư viện nào sẽ bị xem là hỏng, hay liệu có vấn đề khi tương tác với mã khác được biên dịch có dùngCHAR_BITkhông. Tôi đồng ý là nó trái trực giác, nhưng tốt hơn là nên làm công cụ chuyển đổi trước, chứng minh rằng nó an toàn cả trong các trường hợp cực đoan, rồi hẵng chuyển đổiTôi thích vì đây là một đề xuất không thể tranh cãi mà vẫn cực kỳ cay
Việc buộc
int8_t == char == 8 bitsthì hoàn toàn ổn, nhưng tôi không chắc về việc lan truyền hiểu lầm rằng byte là 8 bitByte 8-bit được gọi là octet. Đồng thời, từ C++17,
bytevốn đã là một thứ giống như “bí danh” củachar: https://en.cppreference.com/w/cpp/types/byteTrong 45 năm sau đó, tôi chưa từng thấy “byte” được dùng theo nghĩa khác, nên nếu có định nghĩa “byte” không phải 8 bit thì cần nguồn dẫn
Đây không phải là phát biểu mô tả mà là phát biểu quy phạm
int8 == signed charstd::cout << (int8_t)32 << std::endl;hiển nhiên phải in ra 32Không liên quan đến C++, nhưng tôi khá thích ý tưởng về một microcomputer retro dùng byte 6 bit. Kiểu như 24 bit sẽ trở thành một word
Microcomputer thường xử lý số lượng đối tượng ít và chuộng mảng hơn con trỏ, nên có thể tiết kiệm bộ nhớ. VGA từng dùng 6 bit cho mỗi màu, có thể tạo bảng chữ cái đọc được bằng ma trận bit 6x4, các ngôn ngữ LISP hoặc Forth cơ bản cũng có thể nhét vào bảng chữ cái 6 bit, và System/360 ban đầu cũng chỉ dùng địa chỉ 24 bit. 12MiB bộ nhớ với đơn vị 6 bit có thể định địa chỉ độc lập hẳn là đủ cho bất kỳ ai. Nếu thiếu thì có thể mở rộng FAT-12 một cách tự nhiên thành FAT-24, hoặc dùng con trỏ 48 bit hữu dụng chẳng kém gì con trỏ 64 bit