3 điểm bởi GN⁺ 2024-10-16 | 1 bình luận | Chia sẻ qua WhatsApp
  • Bản sửa đổi Modern C được cập nhật theo chuẩn C mới đã được phát hành để tải xuống miễn phí, giúp người học và người tra cứu ngôn ngữ C xem lại tài liệu theo chuẩn C23
  • Trọng tâm của lần sửa đổi là phản ánh C23, tập trung vào việc hỗ trợ chuyển đổi sang chuẩn mới, đồng bộ với quy trình và thời điểm xuất bản của ISO
  • Các bản phát hành mới của những trình biên dịch lớn đã triển khai hầu hết tính năng của C23, nên các thay đổi trong sách không chỉ là phần giới thiệu mang tính thử nghiệm mà còn gắn với môi trường sử dụng thực tế
  • Các thay đổi về ngôn ngữ và thư viện như _BitInt(N), nullptr, auto, typeof, constexpr được phản ánh rộng rãi, ảnh hưởng cả đến cách viết mã C hiện có
  • Phụ lục chuyển đổi và các header include tạm thời đã được bổ sung để có thể thử ngay trên các nền tảng hiện có, nhưng MEAP của Manning vẫn đang tiếp diễn

Tài liệu miễn phí công khai và tài liệu chuẩn

  • Modern C phiên bản C23 có thể được tải xuống miễn phí
  • Có thể tìm thấy các tài liệu liên quan trên trang riêng của cuốn sách
  • Bản sửa đổi đã chỉnh sửa nhiều phần giải thích, nhưng mục tiêu cốt lõi là phản ánh chuẩn C mới, C23
  • Trong số các tài liệu có thể truy cập công khai, tài liệu gần nhất với nội dung của chuẩn mới là PDF N3220
  • Các bản phát hành mới của những trình biên dịch lớn đã triển khai hầu hết các tính năng mới mà C23 mang lại

Thay đổi ngôn ngữ C23 và hỗ trợ chuyển đổi

  • Các thay đổi liên quan đến số nguyên chiếm tỷ trọng lớn
    • Bổ sung kiểu có độ chính xác bit mới _BitInt(N)
    • Bổ sung các header thư viện C mới cho phép tính số học có kiểm tra tràn và thao tác bit
    • Phản ánh khả năng có kiểu 128 bit trên các kiến trúc hiện đại
    • Bao gồm cả những cải tiến thực chất đối với kiểu enum
  • Cũng bao gồm các khái niệm mới khác của C23
    • Hằng nullptr và kiểu nền tảng của nó
    • Chú thích cú pháp thông qua attributes
    • Công cụ lập trình generic theo kiểu dữ liệu, bao gồm autotypeof
    • Khởi tạo mặc định bằng {} cũng áp dụng cho mảng có độ dài biến đổi
    • constexpr cho các hằng có tên thuộc mọi kiểu
  • Tài liệu mới còn đề cập đến biểu thức phức hợp, lambda, “quốc tế hóa” và cách tiếp cận toàn diện đối với lỗi chương trình
  • Phụ lục và header include tạm thời đã được bổ sung để có thể bắt đầu dùng C23 ngay trên các nền tảng hiện có
  • MEAP cho phiên bản mới của Manning vẫn đang mở

1 bình luận

 
GN⁺ 2024-10-16
Ý kiến trên Hacker News
  • Khác với little endian, thứ tự lưu trữ trên máy của tôi, cách biểu diễn đặt byte bậc cao lên trước được gọi là big endian; nhưng nói rằng cả hai đều được dùng phổ biến trên các bộ xử lý hiện đại thì hơi phóng đại, vì ngoài s390x gần như chẳng còn gì
    Chắc sắp có các bình luận về những kiến trúc big endian ngách/đã chết yểu mà mọi người yêu thích

    • Arm là bi-endian và vẫn sống khỏe trên hầu hết điện thoại
      Như một bình luận hàng đầu khác đã nói, “hiện đại” không nhất thiết có nghĩa là phổ biến hay được dùng rộng rãi
    • POWER là bi-endian. Linux on POWER gần đây là little endian, còn trước kia big endian Linux on POWER khá phổ biến, nhưng vài năm trước các bản phân phối đã chuyển đổi. Ngược lại, AIX và IBM i là big endian
      AIX và IBM i có lẽ không còn sôi động như mainframe của IBM, nhưng có thể xem AIX còn sống hơn cả Solaris hay HP/UX, nhất là khi so với vô số Unix thương mại trước đây. IBM i cũng đang cầm cự, nhưng vẫn sống hơn nhiều so với các nền tảng midrange legacy cạnh tranh như HP MPE, vốn đã chính thức bị nhà cung cấp chấm dứt hỗ trợ
    • MIPS vẫn còn khá sống trong phần cứng mạng tiêu dùng
    • Theo một cách nào đó, cũng có thể nói mọi bộ xử lý đều dùng phổ biến. Vì network byte order là big endian
    • Sparc không phải big endian à?
  • Khía cạnh quan trọng nhất của C là tính di động. Điểm cốt lõi là chạy được từ các vi điều khiển nhỏ đến gần như mọi nền tảng điện toán, nhưng tôi nghi ngờ liệu phiên bản C mới có được chấp nhận đến mức đó không
    Nếu muốn dùng thứ tiên tiến nhất, tôi nghĩ mình sẽ chọn C++2x hoặc Rust hơn là C. Tôi có bỏ sót điều gì không? Tôi tò mò lợi ích mà cái gọi là C hiện đại này mang lại là gì

    • Một trong những lợi thế của việc viết mã C là không phải sa vào những cuộc tranh luận phiền phức như mã idiomatic nên trông ra sao, hay nên dùng tập con ngôn ngữ nào
      Nếu muốn thứ tiên tiến, tôi khuyên dùng Zig. Độ phức tạp của ngôn ngữ thấp hơn nhiều so với C++ hiện đại và Rust. Một tác dụng phụ tốt nhưng ít nổi bật của C23 là nó làm các cú pháp như ... = {}{0} khớp hơn với C++, khiến người bảo trì thư viện C đỡ phiền hơn khi hỗ trợ những người muốn build mã C bằng trình biên dịch C++
    • Cuối cùng nó sẽ được chấp nhận, như C11, các phần mở rộng GNU, và những tính năng riêng lẻ nay đã vào C23 từng được chấp nhận trong quá khứ. Ví dụ, ký pháp nhị phân 0b được dùng rộng rãi trong thế giới vi điều khiển
      Toolchain cho vi điều khiển phần lớn được xây trên GCC nên gần như nhận tính năng miễn phí. Vẫn có các trình biên dịch C độc quyền luôn tụt hậu, nhưng chúng không còn quan trọng như 20 năm trước
    • Những tính năng này rồi sẽ chảy vào dòng chính. Tương tự như C11 hiện nay đang diễn ra
      Nếu không nhắm đến embedded hoặc một tập kiến trúc rất rộng, cũng chẳng có lý do gì để không dùng C23 ngay từ hôm nay
    • Bộ chỉ định thread_local đã được dùng trên vài nền tảng vi điều khiển, nhưng trước C11 thì hoàn toàn bất hợp pháp. Dù vậy, nó đơn giản hóa quản lý bộ nhớ trong môi trường có luồng rất nhiều
      Có cần phải bước hẳn sang thế giới C++ chỉ vì điều đó không?
    • Nếu là mục tiêu mà LLVM/GCC hỗ trợ thì chẳng phải sẽ tự động có phiên bản C mới sao? Chỉ là với các toolchain GCC cũ do vendor chỉnh sửa cho kiến trúc khác thì sẽ không có
  • Cá nhân tôi thấy những thứ như guard, defer, auto, constexpr, nullptr làm C phức tạp hơn nhiều. Tôi chọn C vì cần sự đơn giản; nếu muốn phức tạp thì thường, dù không muốn, tôi sẽ chọn C++, hoặc thà chọn Go, còn nếu là server thì chọn Elixir
    _BitInt(N) cũng xấu, may mà nó gợi nhớ đến _Bool nay đã thành bool. constexprnullptr mang mùi C++ quá nặng. Dù vậy Modern C là một cuốn sách tuyệt vời, và tôi đã dùng rất tốt cho C99, thứ tôi dự định tiếp tục bám lấy

    • Câu hỏi NULL có vấn đề gì sẽ có câu trả lời nếu đọc tài liệu liên quan, nhờ một trong số ít ưu điểm của chuẩn hóa ISO: https://wg21.link/p2312
      Tóm lại, khi truyền đối số NULL vào macro type-generic có thể sinh ra kết quả bất ngờ, và trạng thái của các biểu thức điều kiện như (1 ? 0 : NULL)(1 ? 1 : NULL) phụ thuộc vào cách định nghĩa NULL. Ngoài ra, truyền NULL vào hàm variadic đang kỳ vọng con trỏ có thể gây hậu quả nghiêm trọng. Trên nhiều kiến trúc ngày nay, kích thước intvoid* khác nhau, nên nếu NULL chỉ là 0 thì đối số có kích thước sai sẽ được truyền vào hàm
    • Tôi cho rằng độ phức tạp không tăng tuyến tính. Trái lại, công cụ phức tạp hơn thường làm quá trình và kết quả cuối cùng đơn giản hơn
      Giống như xây nhà. Búa và tua vít rất đơn giản, còn cần cẩu cực kỳ phức tạp, nhưng thứ làm việc xây nhà đơn giản hơn lại là cần cẩu. Nếu chỉ dùng búa và tua vít để xây nhà thì phải nghĩ ra một quy trình cực kỳ phức tạp
      Ngôn ngữ lập trình cũng vậy. Tạo container generic trong C++ là chuyện nhỏ, nhưng trong C thì rất khó. Có thể phần nào bắt chước bằng void * và ép kiểu thủ công, nhưng rườm rà, dễ lỗi và làm mã phức tạp hơn
      std::sortqsort cũng tương tự. Nhờ sức mạnh của template và function object, phần triển khai đơn giản hơn và nhanh hơn. Không cần truyền void * rồi dereference lúc chạy, và có thể đặt phép so sánh ngay trong định nghĩa hàm. Không có lời gọi gián tiếp, không truyền qua stack, thậm chí còn có thể inline hàm so sánh. Độ phức tạp của ngôn ngữ không đồng nghĩa với độ phức tạp của triển khai
    • auto chủ yếu hữu ích khi đụng đến macro type-generic, nhưng trong mã thông thường thì tốt nhất là không dùng. Hy vọng tránh được cơn điên almost always auto từng thịnh hành một thời trong thế giới C++
      Đáng tiếc là mỗi trình biên dịch lại có khác biệt nhỏ. Theo tôi nhớ, Clang triển khai auto kiểu C++, còn GCC triển khai auto kiểu C, nên có khác biệt tinh vi với con trỏ auto. Tôi không biết khác biệt đó giờ đã được sửa chưa
      _BitInt(N) thường không được dùng trực tiếp mà sẽ typedef thành độ rộng cần thiết. Ví dụ dùng như typedef _BitInt(2) u2;. Cú pháp xấu như _B là cần thiết vì tổ hợp dấu gạch dưới theo sau bởi chữ hoa được chuẩn C dành riêng, để khi thêm tính năng nhỏ vào ngôn ngữ không va chạm với mã hiện có. Tên _Bool cũng vì lý do tương tự. Theo tôi biết, defer thực ra đã không vào C23
  • Định nghĩa trước đây thậm chí không chỉ rõ NULL là con trỏ hay số nguyên. Vì vậy, trên những nền tảng không tuân theo yêu cầu ((void*)0) của POSIX, đó là một cái bẫy dưới chân không phải kiểu con trỏ cũng chẳng phải kích thước con trỏ.
    Việc constexprnullptr mang mùi C++ có lẽ là vì chúng được nhập ngược từ C++. Dù vậy vẫn có thể tiếp tục dùng NULL, và nhìn bề ngoài có vẻ nó đã được định nghĩa lại thành nullptr.

    • Đoạn mã này có lỗi, và trên một số kiến trúc có thể bị crash: execlp("echo", "echo", "Hello, world!", NULL);
      Đoạn mã này không có lỗi đó: execlp("echo", "echo", "Hello, world!", nullptr);
      Cách này cũng ổn: execlp("echo", "echo", "Hello, world!", (char *)NULL);
  • Định hỏi xem có danh sách sách C hay nào không thì tự tìm được câu trả lời. Ở đây Modern C được xếp vào mức trung cấp.
    https://stackoverflow.com/questions/562303/the-definitive-c-...

    • Tôi thích Modern C và thấy nó được đánh giá tốt ở nhiều nơi. Tôi đồng ý là sách ở mức trung cấp.
      Với vai trò sách đồng hành C hiện đại cho K&R, tôi cho rằng 21st Century C của Ben Klemens và C Programming: A Modern Approach của King là những lựa chọn thay thế dễ tiếp cận hơn.
    • Fluent C: Principles, Practices and Patterns của Christopher Preschern cũng đáng xem.
    • Danh sách này chưa đầy đủ. Ví dụ thiếu Effective C: https://nostarch.com/effective-c-2nd-edition
      Cá nhân tôi thích Effective C hơn Modern C. Modern C rất chặt chẽ, đọc giống một bản đặc tả ngôn ngữ có chú thích, thứ có thể cần thiết với chuyên gia, nhưng với người chỉ dùng C nhẹ nhàng như tôi thì khá nhàm chán.
  • Tôi khá thích một số phần mở rộng High C của Metaware.
    https://news.ycombinator.com/item?id=41647843
    https://news.ycombinator.com/item?id=38938402

  • Hơn một năm qua tôi dùng C++ hiện đại cho một dự án cá nhân là trình thông dịch ngôn ngữ, nhưng vì gánh nặng tinh thần và vấn đề công cụ của C++, tôi cứ nghĩ đến việc chuyển sang C. IntelliSense của Visual Studio đến giờ vẫn gần như không hoạt động khi dùng module C++20, và do những thất bại của ngôn ngữ, quá nhiều thứ bị đẩy ra interface khiến thời gian biên dịch cũng trở nên khó coi.
    Ngược lại, tôi đã quá quen với class, hàm thành viên, lập trình generic, namespace, nên cũng có cảm giác mình đã mắc kẹt rồi.

    • Tôi đã dùng C++ từ lâu, và hoàn toàn không có ý định từ bỏ destructor để chuyển sang C.
      Với nhu cầu đó, bạn đã cân nhắc C# chưa? Visual Studio hợp với C# hơn nhiều.
  • Khi nhấp vào liên kết mục lục ở thanh bên trong macOS Preview, chúng không hoạt động đúng.

    • Tôi đã thử vài liên kết trong mục lục bằng trình đọc PDF zathura và chúng hoạt động tốt.
    • Mục lục hiện tại chắc chắn đang bị lỗi.
  • Chỉ mới vài năm gần đây tôi mới có thể tin rằng các trình biên dịch C đều hỗ trợ C99 trong thư viện mình đang duy trì: https://github.com/eyalroz/printf
    Nhưng rồi chỉ vài năm sau, đúng như dự đoán, lại có một issue được mở để yêu cầu tương thích C89 vì một toolchain nhúng cổ lỗ sĩ nào đó. Vì vậy C23 thì hay đấy, nhưng cảm giác là khoảng 20 năm nữa hãy nói tiếp.

  • Có ai có thể link một bài giải thích, ở góc độ thực tế, vì sao C về cơ bản bị kẹt ở C99 không? Trong các dự án đáng bàn, hầu như không thấy dự án nào tận dụng tính năng sau C11.

    • C99 vẫn còn mới mà. Microsoft đã cố giết C bằng cách từ chối triển khai những gì không có cả trong C++. MSVC triển khai C99 muộn 16 năm, và chỉ triển khai ở mức tối thiểu. C11 cũng được triển khai muộn 11 năm.
      Vì C trên thực tế đã bị đóng băng trong nhiều thập kỷ, có vẻ cộng đồng người dùng cũng tự chọn lọc thành những người thích C đúng như hiện tại và không ngại hỗ trợ các trình biên dịch tạp nham cổ xưa. Những ai mất kiên nhẫn hoặc muốn một ngôn ngữ của thế kỷ 21 đã chuyển sang C++/Rust/Zig, v.v.
    • Microsoft về cơ bản đã cản trở C99 khi gần như không triển khai các tính năng C99 trong trình biên dịch C của Visual Studio cho đến khoảng năm 2015. Và mãi đến năm 2019 họ mới thừa nhận thất bại và bắt đầu hỗ trợ lại các phiên bản C mới hơn. Frontend C của MSVC đến giờ vẫn tụt hậu một cách ổn định so với Clang và GCC.
      Vào khoảng năm 2010, MSVC vẫn rất quan trọng, điều này nghe có vẻ lạ nếu nhìn từ góc độ ngày nay khi phần lớn lập trình viên dường như đã chuyển sang Linux. Mặt khác, cũng không có nhiều dự án thực sự cần các tính năng C11. C11 còn lấy mất VLA khỏi C99, nhưng đó không phải là mất mát đáng tiếc lắm. C23 có thể là phiên bản đầu tiên kể từ C99 mà nhiều codebase C thực sự đáng nâng cấp lên.