3 คะแนน โดย GN⁺ 2024-10-16 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • Modern C ฉบับปรับปรุง ที่ปรับให้เข้ากับมาตรฐาน C ใหม่ เปิดให้ดาวน์โหลดฟรีแล้ว ทำให้สามารถกลับมาเรียนรู้และใช้อ้างอิงภาษา C ตามเกณฑ์ C23 ได้
  • แกนหลักของการปรับปรุงคือ การรองรับ C23 โดยมุ่งช่วยให้เปลี่ยนผ่านไปสู่มาตรฐานใหม่ให้สอดคล้องกับกระบวนการและช่วงเวลาการเผยแพร่ของ ISO
  • รีลีสใหม่ของคอมไพเลอร์หลัก ๆ ได้ implement ฟีเจอร์ส่วนใหญ่ของ C23 แล้ว ทำให้การเปลี่ยนแปลงในหนังสือเชื่อมโยงกับสภาพแวดล้อมใช้งานจริง ไม่ใช่แค่การแนะนำเชิงทดลอง
  • มีการสะท้อนการเปลี่ยนแปลงของภาษาและไลบรารีอย่างกว้างขวาง เช่น _BitInt(N), nullptr, auto, typeof, constexpr ซึ่งส่งผลต่อวิธีเขียนโค้ด C แบบเดิมด้วย
  • มีการเพิ่ม ภาคผนวกสำหรับการเปลี่ยนผ่านและเฮดเดอร์ include ชั่วคราว เพื่อให้ลองใช้บนแพลตฟอร์มเดิมได้ทันที แต่ MEAP ของ Manning ยังอยู่ระหว่างดำเนินการ

เอกสารเผยแพร่ฟรีและเอกสารมาตรฐาน

  • Modern C รุ่น C23 สามารถดาวน์โหลดได้ฟรี
  • สามารถค้นหาเอกสารที่เกี่ยวข้องได้จากหน้าเฉพาะของหนังสือ
    • หน้าเฉพาะ: https://gustedt.gitlabpages.inria.fr/modern-c/
    • หน้านี้ยังมีลิงก์ดาวน์โหลด ตัวอย่างโค้ด ที่มาพร้อมกับหนังสือด้วย
  • ฉบับปรับปรุงได้แก้ไขคำอธิบายหลายส่วน แต่เป้าหมายหลักคือการสะท้อนมาตรฐาน C ใหม่อย่าง C23
  • ในบรรดาเอกสารที่เข้าถึงได้แบบสาธารณะ แหล่งที่ใกล้เคียงกับเนื้อหามาตรฐานใหม่ที่สุดคือ N3220 PDF
  • รีลีสใหม่ของคอมไพเลอร์หลัก ๆ ได้ implement ฟีเจอร์ใหม่ส่วนใหญ่ที่ C23 นำมาแล้ว

การเปลี่ยนแปลงของภาษา C23 และการสนับสนุนการเปลี่ยนผ่าน

  • การเปลี่ยนแปลงเกี่ยวกับจำนวนเต็มมีสัดส่วนมาก
    • เพิ่มชนิดข้อมูลความละเอียดบิตใหม่ _BitInt(N)
    • เพิ่มเฮดเดอร์ไลบรารี C ใหม่สำหรับ arithmetic ที่ตรวจสอบ overflow และการจัดการบิต
    • สะท้อนความเป็นไปได้ของ ชนิดข้อมูล 128 บิต บนสถาปัตยกรรมสมัยใหม่
    • รวมการปรับปรุงเชิงปฏิบัติของชนิด enum
  • มีแนวคิดใหม่อื่น ๆ ของ C23 รวมอยู่ด้วย
    • ค่าคงที่ nullptr และชนิดข้อมูลพื้นฐานของมัน
    • คำอธิบายประกอบไวยากรณ์ผ่าน attributes
    • เครื่องมือสำหรับ type-generic programming ซึ่งรวมถึง auto และ typeof
    • การกำหนดค่าเริ่มต้นพื้นฐานด้วย {} ที่ใช้ได้กับอาร์เรย์ความยาวแปรผันด้วย
    • constexpr สำหรับค่าคงที่แบบมีชื่อของทุกชนิดข้อมูล
  • เอกสารใหม่ยังครอบคลุม expression แบบผสม, lambda, “internationalization” และแนวทางแบบครอบคลุมต่อความล้มเหลวของโปรแกรม
  • มีการเพิ่ม ภาคผนวกและเฮดเดอร์ include ชั่วคราว เพื่อให้เริ่มใช้ C23 บนแพลตฟอร์มเดิมได้ทันที
  • MEAP ของฉบับใหม่จาก Manning ยังเปิดอยู่

1 ความคิดเห็น

 
GN⁺ 2024-10-16
ความคิดเห็นจาก Hacker News
  • ต่างจาก little-endian ซึ่งเป็นลำดับการจัดเก็บข้อมูลของเครื่องผม วิธีที่เก็บเลขหลักสูงไว้ก่อนเรียกว่า big-endian แต่จะบอกว่ายังใช้กันทั่วไปทั้งสองแบบในโปรเซสเซอร์สมัยใหม่นั้นก็ดูเวอร์ไปหน่อย เพราะแทบไม่เหลืออะไรนอกจาก s390x แล้ว
    อีกเดี๋ยวก็คงมีคอมเมนต์แนวสถาปัตยกรรม big-endian แบบเฉพาะกลุ่ม/ใกล้สูญพันธุ์ที่ทุกคนชอบโผล่มาแน่

    • Arm เป็นแบบ bi-endian และยังมีชีวิตอยู่บนมือถือส่วนใหญ่
      เหมือนคอมเมนต์อันดับบน ๆ อื่น ๆ คำว่า modern ไม่ได้แปลว่าต้องนิยมหรือถูกใช้อย่างแพร่หลายเสมอไป
    • POWER เป็นแบบ bi-endian เช่นกัน Linux on POWER รุ่นใหม่เป็น little-endian และเมื่อก่อน Big-endian Linux on POWER เคยพบได้ทั่วไป แต่ดิสโทรต่าง ๆ เปลี่ยนไปเมื่อไม่กี่ปีก่อน ในขณะที่ AIX และ IBM i ยังเป็น big-endian
      AIX กับ IBM i อาจไม่ได้คึกคักเท่า IBM mainframe แต่ก็ยังถือว่า AIX มีชีวิตมากกว่า Solaris หรือ HP/UX เสียอีก และยิ่งชัดเมื่อเทียบกับ Unix เชิงพาณิชย์จำนวนมากในอดีต ส่วน IBM i ก็ยังพอประคองอยู่ แต่ยังมีชีวิตกว่าคู่แข่งกลุ่มแพลตฟอร์มมิดเรนจ์แบบ legacy ที่ผู้ขายยุติการสนับสนุนอย่างเป็นทางการไปแล้ว เช่น HP MPE มาก
    • MIPS ยังมีชีวิตอยู่พอสมควรในฮาร์ดแวร์เครือข่ายสำหรับผู้บริโภค
    • มองอีกแบบก็อาจบอกได้ว่าโปรเซสเซอร์ทุกตัวใช้งานกันทั่วไป เพราะ network byte order เป็นแบบ big-endian
    • Sparc ไม่ใช่ big-endian เหรอ?
  • แง่มุมที่สำคัญที่สุดของ C คือ portability หัวใจคือมันไปได้ตั้งแต่ไมโครคอนโทรลเลอร์ขนาดเล็กจนถึงแทบทุกแพลตฟอร์มคอมพิวติ้ง แต่ก็สงสัยว่า C เวอร์ชันใหม่จะถูกยอมรับได้มากขนาดนั้นไหม
    ถ้าอยากใช้ของล้ำสมัย ผมน่าจะเลือก C++2x หรือ Rust มากกว่า C ผมพลาดอะไรไปหรือเปล่า? อยากรู้ว่าข้อดีของ C สมัยใหม่ที่เรียกกันนี้คืออะไร

    • ข้อดีอย่างหนึ่งของการเขียนโค้ด C คือไม่ต้องไปเถียงเรื่องน่าปวดหัวอย่างโค้ดแบบ idiomatic ควรหน้าตาอย่างไร หรือควรใช้ส่วนย่อยของภาษาแบบไหน
      ถ้าจะเอาของล้ำสมัย ผมแนะนำ Zig ความซับซ้อนของภาษาต่ำกว่า C++ สมัยใหม่และ Rust มาก ผลข้างเคียงดี ๆ ที่ไม่ค่อยเด่นของ C23 คือมันทำให้ไวยากรณ์อย่าง ... = {} และ {0} สอดคล้องกับ C++ มากขึ้น เลยทำให้ผู้ดูแลไลบรารี C รองรับคนที่อยาก build โค้ด C ด้วยคอมไพเลอร์ C++ ได้ง่ายขึ้นและน่ารำคาญน้อยลง
    • สุดท้ายมันก็จะถูกรับไปใช้ เหมือนที่ C11, ส่วนขยายของ GNU และฟีเจอร์รายตัวที่ตอนนี้เข้า C23 ไปแล้วเคยเป็นมา ตัวอย่างเช่นรูปแบบเลขฐานสอง 0b ถูกใช้กันแพร่หลายในโลกไมโครคอนโทรลเลอร์
      โดยทั่วไป toolchain ของไมโครคอนโทรลเลอร์สร้างอยู่บน GCC อยู่แล้ว เลยได้ฟีเจอร์มาแบบฟรี ๆ ยังมีคอมไพเลอร์ C แบบ proprietary ที่ตามหลังอยู่เสมอ แต่ก็ไม่สำคัญเท่าเมื่อ 20 ปีก่อนแล้ว
    • ฟีเจอร์พวกนี้สุดท้ายก็จะไหลเข้าสู่กระแสหลัก คล้ายกับที่ตอนนี้ C11 กำลังเป็นอยู่
      ถ้าคุณไม่ได้เจาะจงไปที่ embedded หรือชุดสถาปัตยกรรมที่กว้างมาก ก็ไม่มีเหตุผลอะไรที่วันนี้จะใช้ C23 ไม่ได้
    • ตัวระบุ thread_local ถูกใช้บนบางแพลตฟอร์มไมโครคอนโทรลเลอร์อยู่แล้ว แต่ถ้าย้อนก่อน C11 มันถือว่าผิดกฎเต็ม ๆ ถึงอย่างนั้นมันก็ช่วยทำให้ memory management ในสภาพแวดล้อมแบบเธรดง่ายขึ้นมาก
      แล้วมีเหตุผลอะไรที่ต้องย้ายเข้าไปอยู่ในโลกของ C++ เพื่อสิ่งนี้ด้วย?
    • ถ้าเป็นเป้าหมายที่ LLVM/GCC รองรับอยู่แล้ว ก็ไม่ได้แปลว่าจะได้ C เวอร์ชันใหม่ไปโดยอัตโนมัติหรอกหรือ? เพียงแต่จะไม่ได้ใน toolchain GCC เก่า ๆ ที่ผู้ขายปรับแต่งไว้สำหรับสถาปัตยกรรมอื่น
  • โดยส่วนตัวผมคิดว่าพวก guard, defer, auto, constexpr, nullptr อะไรทำนองนี้ทำให้ C ซับซ้อนขึ้นมาก ผมเลือก C เพราะต้องการความเรียบง่าย ถ้าต้องการความซับซ้อน ปกติก็คงเลือก C++ แม้จะไม่อยากเลือกก็ตาม หรือไม่ก็เลือก Go หรือถ้าเป็นฝั่งเซิร์ฟเวอร์ก็คงเลือก Elixir มากกว่า
    _BitInt(N) ก็ดูไม่สวย และทำให้นึกถึง _Bool ซึ่งโชคดีที่ตอนนี้กลายเป็น bool แล้ว constexpr กับ nullptr ก็มีกลิ่น C++ แรงเกินไป ถึงอย่างนั้น Modern C ก็เป็นหนังสือที่ยอดเยี่ยม และผมก็ใช้มันได้ดีมาตลอดกับงาน C99 ที่ตั้งใจจะใช้ต่อไป

    • ถ้าถามว่า NULL มีปัญหาอะไร แค่อ่านเอกสารที่เกี่ยวข้องก็ได้คำตอบแล้ว ซึ่งเป็นหนึ่งในข้อดีไม่กี่อย่างของการทำมาตรฐาน ISO: https://wg21.link/p2312
      สรุปคือ เวลาส่งอาร์กิวเมนต์ NULL เข้าไปใน type-generic macro อาจเกิดผลลัพธ์ที่น่าประหลาดใจ และสถานะของ conditional expression อย่าง (1 ? 0 : NULL) กับ (1 ? 1 : NULL) ก็เปลี่ยนได้ตามวิธีนิยาม NULL นอกจากนี้ ถ้าส่ง NULL เข้าไปให้ฟังก์ชัน variadic ที่คาดหวังพอยน์เตอร์ ก็อาจเกิดผลร้ายแรงได้ ทุกวันนี้หลายสถาปัตยกรรมมีขนาด int กับ void* ไม่เท่ากัน ดังนั้นถ้า NULL เป็นแค่ 0 ฟังก์ชันก็จะได้รับอาร์กิวเมนต์ที่มีขนาดผิด
    • ผมไม่คิดว่าความซับซ้อนเพิ่มขึ้นแบบเส้นตรง ตรงกันข้าม เครื่องมือที่ซับซ้อนกว่ามักทำให้กระบวนการและผลลัพธ์สุดท้ายเรียบง่ายขึ้น
      มันคล้ายกับการสร้างบ้าน ค้อนกับไขควงนั้นง่ายมาก ส่วนเครนนั้นซับซ้อนสุด ๆ แต่สิ่งที่ทำให้การสร้างบ้านง่ายขึ้นคือเครน ถ้าจะสร้างบ้านด้วยแค่ค้อนกับไขควง คุณต้องคิดขั้นตอนที่ซับซ้อนมหาศาลขึ้นมาเอง
      ภาษาโปรแกรมก็เหมือนกัน การสร้าง generic container ใน C++ เป็นเรื่องเล็กน้อย แต่ใน C ยากมาก คุณพอจะเลียนแบบได้บ้างด้วย void * และการ cast ด้วยมือ แต่ทั้งยุ่งยาก ผิดพลาดง่าย และทำให้โค้ดซับซ้อนขึ้น
      std::sort กับ qsort ก็เช่นกัน ด้วยพลังของ template และ function object ทำให้ implementation ทั้งง่ายกว่าและเร็วกว่า ไม่ต้องส่ง void * แล้ว dereference ตอนรันไทม์ และยังใส่การเปรียบเทียบไว้ในตัวนิยามฟังก์ชันได้เลย ไม่มี indirect call ไม่มีการส่งผ่านสแตก และยัง inline ฟังก์ชันเปรียบเทียบได้อีก ความซับซ้อนของภาษาไม่ได้แปลว่า implementation จะซับซ้อนเสมอไป
    • auto มีประโยชน์หลัก ๆ ตอนจัดการกับ type-generic macro แต่กับโค้ดทั่วไปไม่ค่อยควรใช้ จะดีกว่าถ้าเลี่ยงความคลั่งแบบ almost always auto ที่เคยฮิตอยู่พักหนึ่งในโลก C++
      น่าเสียดายที่คอมไพเลอร์แต่ละตัวมีความต่างกันเล็กน้อย เท่าที่จำได้ Clang implement auto แบบ C++ ส่วน GCC implement auto แบบ C ทำให้มีความต่างแบบละเอียดอ่อนกับ auto pointer ไม่แน่ใจว่าตอนนี้แก้ให้ตรงกันแล้วหรือยัง
      ปกติ _BitInt(N) จะไม่ค่อยถูกใช้ตรง ๆ แต่จะ typedef เป็นความกว้างที่ต้องการแทน เช่น typedef _BitInt(2) u2; ไวยากรณ์หน้าตาไม่สวยอย่าง _B นั้นจำเป็นเพราะรูปแบบตัวพิมพ์ใหญ่หลังขีดล่างถูกสงวนไว้ในมาตรฐาน C เพื่อหลีกเลี่ยงการชนกับโค้ดเดิมเวลาเพิ่มฟีเจอร์เล็ก ๆ เข้าในภาษา ชื่อ _Bool ก็เป็นเหตุผลเดียวกัน เท่าที่ผมรู้ defer ไม่ได้เข้า C23 จริง ๆ
  • คำนิยามแบบเก่าไม่ได้ระบุด้วยซ้ำว่า NULL เป็นพอยน์เตอร์หรือจำนวนเต็ม ดังนั้นบนแพลตฟอร์มที่ไม่ทำตามข้อกำหนดของ Posix เรื่อง ((void*)0) มันจึงเป็นกับดักใต้เท้าแบบที่ ไม่ใช่ทั้งชนิดพอยน์เตอร์และไม่ใช่ทั้งขนาด
    ที่ constexpr และ nullptr มีกลิ่นอายแบบ C++ ก็คงเพราะถูกนำเข้าย้อนกลับมาจาก C++ นั่นเอง ถึงอย่างนั้นก็ยังใช้ NULL ต่อได้ แต่ดูเผิน ๆ เหมือนมันถูกนิยามใหม่เป็น nullptr

    • โค้ดนี้มีบั๊ก และบนบางสถาปัตยกรรมอาจแครชได้: execlp("echo", "echo", "Hello, world!", NULL);
      โค้ดนี้ไม่มีบั๊กนั้น: execlp("echo", "echo", "Hello, world!", nullptr);
      แบบนี้ก็ใช้ได้เช่นกัน: execlp("echo", "echo", "Hello, world!", (char *)NULL);
  • กำลังจะถามว่ามี รายชื่อหนังสือ C ดี ๆ ไหม แต่สุดท้ายก็หาคำตอบเองได้ ที่นี่จัด Modern C ไว้เป็นระดับกลาง
    https://stackoverflow.com/questions/562303/the-definitive-c-...

    • ชอบ Modern C และเคยเห็นถูกประเมินไว้ดีในหลายที่ เห็นด้วยว่าเป็นระดับกลาง
      ถ้าจะหาเพื่อนคู่หู C ยุคใหม่ให้ K&R มองว่า 21st Century C ของ Ben Klemens และ C Programming: A Modern Approach ของ King เป็นทางเลือกที่เข้าถึงง่ายกว่า
    • Fluent C: Principles, Practices and Patterns ของ Christopher Preschern ก็น่าอ่านเช่นกัน
    • รายชื่อนี้ยังไม่ครบถ้วน ตัวอย่างเช่นไม่มี Effective C: https://nostarch.com/effective-c-2nd-edition
      ส่วนตัวชอบ Effective C มากกว่า Modern C เพราะ Modern C เข้มงวดมากจนให้ความรู้สึกเหมือนกำลังอ่านสเปกที่ใส่หมายเหตุภาษาไว้ ซึ่งอาจจำเป็นสำหรับผู้เชี่ยวชาญ แต่สำหรับคนที่ใช้ C แบบพอประมาณอย่างผม มันอ่านแล้วน่าเบื่อ
  • ค่อนข้างชอบบางส่วนของ ส่วนขยาย High C ของ Metaware
    https://news.ycombinator.com/item?id=41647843
    https://news.ycombinator.com/item?id=38938402

  • ตลอดเวลากว่าหนึ่งปีที่ผ่านมาใช้ C++ สมัยใหม่กับโปรเจกต์ส่วนตัวที่เป็น language interpreter แต่ก็คิดอยู่เรื่อย ๆ ว่าจะเปลี่ยนเป็น C ดีไหม เพราะภาระทางความคิดของ C++ และปัญหาเรื่องเครื่องมือ Visual Studio IntelliSense ยังแทบใช้งานไม่ได้เลยเมื่อใช้ C++20 modules และเพราะความล้มเหลวของภาษา หลายอย่างจึงถูกผลักไปอยู่ที่ interface ทำให้เวลา compile ดูเลวร้ายมาก
    ในทางกลับกันก็ชินกับคลาส เมมเบอร์ฟังก์ชัน generic programming และเนมสเปซมากเกินไป จนเหมือนจะติดกับไปแล้ว

    • ใช้ C++ มานานมาก และไม่คิดจะยอมเสีย destructor ไปเพื่อย้ายไป C เด็ดขาด
      สำหรับจุดประสงค์นั้น เคยพิจารณา C# ไหม? Visual Studio เข้ากับ C# ได้ดีกว่ามาก
  • ใน macOS Preview ถ้าคลิกลิงก์ สารบัญ ในแถบด้านข้าง มันทำงานไม่ถูกต้อง

    • ลองทดสอบบางลิงก์ในสารบัญกับโปรแกรมอ่าน PDF อย่าง zathura แล้วใช้งานได้ปกติ
    • ตอนนี้สารบัญพังแน่นอน
  • เพิ่งจะผ่านมาไม่กี่ปีเองที่สามารถเชื่อได้ว่าคอมไพเลอร์ C ทั้งหมดจะรองรับ C99 ในไลบรารีที่ดูแลอยู่: https://github.com/eyalroz/printf
    แต่พอผ่านไปไม่กี่ปี ก็มี issue เปิดเข้ามาขอความเข้ากันได้กับ C89 เพราะ toolchain embedded โบราณบางตัวตามระเบียบ เพราะงั้น C23 ก็ดีนะ แต่ความรู้สึกคือค่อยมาคุยกันใหม่อีกสัก 20 ปี

  • มีใครช่วยลิงก์บทความที่อธิบายเชิงปฏิบัติได้ไหมว่าทำไม C ถึงแทบจะ หยุดอยู่ที่ C99? แทบไม่เห็นโปรเจกต์ที่น่าพูดถึงไหนใช้ฟีเจอร์หลัง C11 เลย

    • C99 ยังถือว่าใหม่อยู่ Microsoft พยายามฆ่า C ด้วยการปฏิเสธจะ implement สิ่งที่ไม่ได้อยู่ใน C++ ด้วย MSVC รองรับ C99 ช้ากว่า 16 ปีและรองรับแค่ขั้นต่ำ C11 ก็ช้ากว่า 11 ปี
      เพราะ C แทบหยุดนิ่งมาหลายสิบปี ฐานผู้ใช้จึงดูเหมือนคัดเลือกตัวเองให้เหลือคนที่ชอบ C ในแบบที่มันเป็นอยู่ และไม่รังเกียจการรองรับคอมไพเลอร์ขยะโบราณ ส่วนคนที่หมดความอดทนหรืออยากได้ภาษาแห่งศตวรรษที่ 21 ก็ย้ายไป C++/Rust/Zig ฯลฯ กันหมดแล้ว
    • Microsoft แทบไม่รองรับฟีเจอร์ C99 ในคอมไพเลอร์ C ของ Visual Studio จนถึงราวปี 2015 จึงถือว่า ขัดขวาง C99 ไปโดยปริยาย แล้วในปี 2019 ก็ยอมรับความล้มเหลวและเริ่มกลับมารองรับ C เวอร์ชันใหม่กว่าอีกครั้ง ฟรอนต์เอนด์ C ของ MSVC ก็ยังตามหลัง Clang และ GCC อย่างสม่ำเสมอ
      ในราวปี 2010 MSVC ยังสำคัญมาก ซึ่งอาจฟังดูแปลกจากมุมมองที่ทุกวันนี้นักพัฒนาส่วนใหญ่ดูเหมือนจะย้ายไป Linux กันแล้ว ในอีกด้านหนึ่งก็ไม่ได้มีโปรเจกต์มากนักที่จำเป็นต้องใช้ฟีเจอร์ C11 จริง ๆ โดย C11 ยังเอา VLA ออกจาก C99 ด้วย ซึ่งก็ไม่ใช่การสูญเสียที่น่าเสียดายนัก และ C23 อาจเป็นเวอร์ชันแรกหลัง C99 ที่ codebase C จำนวนมากคุ้มจะอัปเกรดจริง ๆ