Modern C รุ่น C23
(gustedt.wordpress.com)- Modern C ฉบับปรับปรุง ที่ปรับให้เข้ากับมาตรฐาน C ใหม่ เปิดให้ดาวน์โหลดฟรีแล้ว ทำให้สามารถกลับมาเรียนรู้และใช้อ้างอิงภาษา C ตามเกณฑ์ C23 ได้
- แกนหลักของการปรับปรุงคือ การรองรับ C23 โดยมุ่งช่วยให้เปลี่ยนผ่านไปสู่มาตรฐานใหม่ให้สอดคล้องกับกระบวนการและช่วงเวลาการเผยแพร่ของ ISO
- รีลีสใหม่ของคอมไพเลอร์หลัก ๆ ได้ implement ฟีเจอร์ส่วนใหญ่ของ C23 แล้ว ทำให้การเปลี่ยนแปลงในหนังสือเชื่อมโยงกับสภาพแวดล้อมใช้งานจริง ไม่ใช่แค่การแนะนำเชิงทดลอง
- มีการสะท้อนการเปลี่ยนแปลงของภาษาและไลบรารีอย่างกว้างขวาง เช่น
_BitInt(N),nullptr,auto,typeof,constexprซึ่งส่งผลต่อวิธีเขียนโค้ด C แบบเดิมด้วย - มีการเพิ่ม ภาคผนวกสำหรับการเปลี่ยนผ่านและเฮดเดอร์ include ชั่วคราว เพื่อให้ลองใช้บนแพลตฟอร์มเดิมได้ทันที แต่ MEAP ของ Manning ยังอยู่ระหว่างดำเนินการ
เอกสารเผยแพร่ฟรีและเอกสารมาตรฐาน
- Modern C รุ่น C23 สามารถดาวน์โหลดได้ฟรี
- ดาวน์โหลด: https://hal.inria.fr/hal-02383654
- สามารถค้นหาเอกสารที่เกี่ยวข้องได้จากหน้าเฉพาะของหนังสือ
- หน้าเฉพาะ: 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 ยังเปิดอยู่
- MEAP: https://www.manning.com/books/modern-c-third-edition
- ยังไม่ทราบช่วงเวลาการตีพิมพ์ขั้นสุดท้ายของ Modern C รุ่น C23 เวอร์ชัน Manning
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
ต่างจาก little-endian ซึ่งเป็นลำดับการจัดเก็บข้อมูลของเครื่องผม วิธีที่เก็บเลขหลักสูงไว้ก่อนเรียกว่า big-endian แต่จะบอกว่ายังใช้กันทั่วไปทั้งสองแบบในโปรเซสเซอร์สมัยใหม่นั้นก็ดูเวอร์ไปหน่อย เพราะแทบไม่เหลืออะไรนอกจาก s390x แล้ว
อีกเดี๋ยวก็คงมีคอมเมนต์แนวสถาปัตยกรรม big-endian แบบเฉพาะกลุ่ม/ใกล้สูญพันธุ์ที่ทุกคนชอบโผล่มาแน่
เหมือนคอมเมนต์อันดับบน ๆ อื่น ๆ คำว่า modern ไม่ได้แปลว่าต้องนิยมหรือถูกใช้อย่างแพร่หลายเสมอไป
AIX กับ IBM i อาจไม่ได้คึกคักเท่า IBM mainframe แต่ก็ยังถือว่า AIX มีชีวิตมากกว่า Solaris หรือ HP/UX เสียอีก และยิ่งชัดเมื่อเทียบกับ Unix เชิงพาณิชย์จำนวนมากในอดีต ส่วน IBM i ก็ยังพอประคองอยู่ แต่ยังมีชีวิตกว่าคู่แข่งกลุ่มแพลตฟอร์มมิดเรนจ์แบบ legacy ที่ผู้ขายยุติการสนับสนุนอย่างเป็นทางการไปแล้ว เช่น HP MPE มาก
แง่มุมที่สำคัญที่สุดของ C คือ portability หัวใจคือมันไปได้ตั้งแต่ไมโครคอนโทรลเลอร์ขนาดเล็กจนถึงแทบทุกแพลตฟอร์มคอมพิวติ้ง แต่ก็สงสัยว่า C เวอร์ชันใหม่จะถูกยอมรับได้มากขนาดนั้นไหม
ถ้าอยากใช้ของล้ำสมัย ผมน่าจะเลือก C++2x หรือ Rust มากกว่า C ผมพลาดอะไรไปหรือเปล่า? อยากรู้ว่าข้อดีของ C สมัยใหม่ที่เรียกกันนี้คืออะไร
ถ้าจะเอาของล้ำสมัย ผมแนะนำ Zig ความซับซ้อนของภาษาต่ำกว่า C++ สมัยใหม่และ Rust มาก ผลข้างเคียงดี ๆ ที่ไม่ค่อยเด่นของ C23 คือมันทำให้ไวยากรณ์อย่าง
... = {}และ{0}สอดคล้องกับ C++ มากขึ้น เลยทำให้ผู้ดูแลไลบรารี C รองรับคนที่อยาก build โค้ด C ด้วยคอมไพเลอร์ C++ ได้ง่ายขึ้นและน่ารำคาญน้อยลง0bถูกใช้กันแพร่หลายในโลกไมโครคอนโทรลเลอร์โดยทั่วไป toolchain ของไมโครคอนโทรลเลอร์สร้างอยู่บน GCC อยู่แล้ว เลยได้ฟีเจอร์มาแบบฟรี ๆ ยังมีคอมไพเลอร์ C แบบ proprietary ที่ตามหลังอยู่เสมอ แต่ก็ไม่สำคัญเท่าเมื่อ 20 ปีก่อนแล้ว
ถ้าคุณไม่ได้เจาะจงไปที่ embedded หรือชุดสถาปัตยกรรมที่กว้างมาก ก็ไม่มีเหตุผลอะไรที่วันนี้จะใช้ C23 ไม่ได้
thread_localถูกใช้บนบางแพลตฟอร์มไมโครคอนโทรลเลอร์อยู่แล้ว แต่ถ้าย้อนก่อน C11 มันถือว่าผิดกฎเต็ม ๆ ถึงอย่างนั้นมันก็ช่วยทำให้ memory management ในสภาพแวดล้อมแบบเธรดง่ายขึ้นมากแล้วมีเหตุผลอะไรที่ต้องย้ายเข้าไปอยู่ในโลกของ C++ เพื่อสิ่งนี้ด้วย?
โดยส่วนตัวผมคิดว่าพวก
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 implementautoแบบ C ทำให้มีความต่างแบบละเอียดอ่อนกับautopointer ไม่แน่ใจว่าตอนนี้แก้ให้ตรงกันแล้วหรือยังปกติ
_BitInt(N)จะไม่ค่อยถูกใช้ตรง ๆ แต่จะtypedefเป็นความกว้างที่ต้องการแทน เช่นtypedef _BitInt(2) u2;ไวยากรณ์หน้าตาไม่สวยอย่าง_Bนั้นจำเป็นเพราะรูปแบบตัวพิมพ์ใหญ่หลังขีดล่างถูกสงวนไว้ในมาตรฐาน C เพื่อหลีกเลี่ยงการชนกับโค้ดเดิมเวลาเพิ่มฟีเจอร์เล็ก ๆ เข้าในภาษา ชื่อ_Boolก็เป็นเหตุผลเดียวกัน เท่าที่ผมรู้deferไม่ได้เข้า C23 จริง ๆคำนิยามแบบเก่าไม่ได้ระบุด้วยซ้ำว่า
NULLเป็นพอยน์เตอร์หรือจำนวนเต็ม ดังนั้นบนแพลตฟอร์มที่ไม่ทำตามข้อกำหนดของ Posix เรื่อง((void*)0)มันจึงเป็นกับดักใต้เท้าแบบที่ ไม่ใช่ทั้งชนิดพอยน์เตอร์และไม่ใช่ทั้งขนาดที่
constexprและnullptrมีกลิ่นอายแบบ C++ ก็คงเพราะถูกนำเข้าย้อนกลับมาจาก C++ นั่นเอง ถึงอย่างนั้นก็ยังใช้NULLต่อได้ แต่ดูเผิน ๆ เหมือนมันถูกนิยามใหม่เป็นnullptrexeclp("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-...
ถ้าจะหาเพื่อนคู่หู C ยุคใหม่ให้ K&R มองว่า 21st Century C ของ Ben Klemens และ C Programming: A Modern Approach ของ King เป็นทางเลือกที่เข้าถึงง่ายกว่า
ส่วนตัวชอบ 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# ไหม? Visual Studio เข้ากับ C# ได้ดีกว่ามาก
ใน macOS Preview ถ้าคลิกลิงก์ สารบัญ ในแถบด้านข้าง มันทำงานไม่ถูกต้อง
เพิ่งจะผ่านมาไม่กี่ปีเองที่สามารถเชื่อได้ว่าคอมไพเลอร์ C ทั้งหมดจะรองรับ C99 ในไลบรารีที่ดูแลอยู่: https://github.com/eyalroz/printf
แต่พอผ่านไปไม่กี่ปี ก็มี issue เปิดเข้ามาขอความเข้ากันได้กับ C89 เพราะ toolchain embedded โบราณบางตัวตามระเบียบ เพราะงั้น C23 ก็ดีนะ แต่ความรู้สึกคือค่อยมาคุยกันใหม่อีกสัก 20 ปี
มีใครช่วยลิงก์บทความที่อธิบายเชิงปฏิบัติได้ไหมว่าทำไม C ถึงแทบจะ หยุดอยู่ที่ C99? แทบไม่เห็นโปรเจกต์ที่น่าพูดถึงไหนใช้ฟีเจอร์หลัง C11 เลย
เพราะ C แทบหยุดนิ่งมาหลายสิบปี ฐานผู้ใช้จึงดูเหมือนคัดเลือกตัวเองให้เหลือคนที่ชอบ C ในแบบที่มันเป็นอยู่ และไม่รังเกียจการรองรับคอมไพเลอร์ขยะโบราณ ส่วนคนที่หมดความอดทนหรืออยากได้ภาษาแห่งศตวรรษที่ 21 ก็ย้ายไป C++/Rust/Zig ฯลฯ กันหมดแล้ว
ในราวปี 2010 MSVC ยังสำคัญมาก ซึ่งอาจฟังดูแปลกจากมุมมองที่ทุกวันนี้นักพัฒนาส่วนใหญ่ดูเหมือนจะย้ายไป Linux กันแล้ว ในอีกด้านหนึ่งก็ไม่ได้มีโปรเจกต์มากนักที่จำเป็นต้องใช้ฟีเจอร์ C11 จริง ๆ โดย C11 ยังเอา VLA ออกจาก C99 ด้วย ซึ่งก็ไม่ใช่การสูญเสียที่น่าเสียดายนัก และ C23 อาจเป็นเวอร์ชันแรกหลัง C99 ที่ codebase C จำนวนมากคุ้มจะอัปเกรดจริง ๆ