มีแนวทางที่ยอมรับกันโดยทั่วไปเกี่ยวกับวิธีการเขียน C สมัยใหม่หรือไม่?


13

ฉันมีพื้นหลัง Java / Groovy ที่แข็งแกร่งและฉันได้รับมอบหมายให้ทำงานกับทีมที่ดูแลฐานรหัส C ที่ค่อนข้างใหญ่สำหรับซอฟต์แวร์การดูแลระบบ

บางจุดปวดเช่นการจัดการกับหยดในฐานข้อมูลหรือการสร้างรายงานใน PDF และ Excel ได้รับการส่งไปยังบริการเว็บ java

อย่างไรก็ตามในฐานะที่เป็น Java dev ฉันรู้สึกสับสนเล็กน้อยกับบางส่วนของรหัส:

  • มันเป็น verbose (โดยเฉพาะอย่างยิ่งเมื่อจัดการกับ 'ข้อยกเว้น')
  • มีวิธีการขนาดใหญ่มากมาย (วิธีการ 2000 + บรรทัดหลายวิธี)
  • ไม่มีโครงสร้างข้อมูลขั้นสูง (ฉันพลาดรายการตั้งค่าและแผนที่เป็นจำนวนมาก)
  • ไม่มีการแยกข้อกังวล (SQL ผสมปนเปกันอย่างสนุกสนาน)

ด้วยเหตุนี้ฉันจึงรู้สึกว่าธุรกิจถูกซ่อนอยู่ในรหัสทางเทคนิคและสมองของฉันที่ถูกสร้างขึ้นด้วย Object Oriented และการเขียนโปรแกรมเชิงฟังก์ชั่น

ข้อดีของโครงการคือรหัสตรงไปตรงมา: ไม่มีเฟรมเวิร์กไม่มีการจัดการโค้ดไบต์ที่รันไทม์ไม่มี AOP และเซิร์ฟเวอร์สามารถตอบผู้ใช้มากกว่า 10,000 คนพร้อมกันด้วยเครื่องเดียวโดยใช้หน่วยความจำน้อยกว่าจาวาต้องคาย "hello world"

ฉันต้องการเรียนรู้วิธีการเขียนรหัส C ตามหลักการสมัยใหม่ที่เป็นที่ยอมรับกันทั่วไป มีหลักการใด ๆ ที่เป็นที่ยอมรับกันโดยทั่วไปเกี่ยวกับวิธีการเขียนและโครงสร้าง C ที่ทันสมัย?

บางสิ่งบางอย่างคล้ายกับหนังสือ 'Effective Java' แต่สำหรับ C

แก้ไขเมื่อมีแสงสว่างของคำตอบและความคิดเห็น:

  • ฉันจะพยายามปรับความคิดของฉันให้เป็นรหัส C และไม่ลองสะท้อนกับ OOP
  • ฉันเริ่มสแกน - อ่านคู่มือการเขียนโค้ดสไตล์ที่แนะนำจากความคิดเห็น (มาตรฐานการเข้ารหัสของ GNU และสไตล์การเข้ารหัสเคอร์เนล Linux)
  • จากนั้นฉันจะพยายามเสนอสไตล์รหัสนี้ให้เพื่อนร่วมงานของฉัน ส่วนที่ยากที่สุดคือการโน้มน้าวให้เพื่อนร่วมงานทราบว่าวิธีการขนาดใหญ่สามารถแบ่งเป็นส่วนเล็ก ๆ และการทำซ้ำรหัสข้อผิดพลาด 4 บรรทัดเดียวกันนั้นสามารถหลีกเลี่ยงได้ด้วยความช่วยเหลือของวิธีการ

5
แอปพลิเคชั่นต้องการความทันสมัยหรือไม่หรือคุณคิดว่ามันเป็นเพราะวิธีการเขียนนั้นไม่คุ้นเคย?
Blrfl


1
@ Blrfl ฉันรู้สึกว่าแอปพลิเคชันนั้นเขียนด้วยมาตรฐานที่ล้าสมัย ฉันแค่อยากรู้ว่าวันนี้ (2559) มาตรฐานสำหรับอะไร (การบริหาร) C. ถ้ามี ฉันไม่ต้องการ refactor หรือปรับแต่งแอปปัจจุบันฉันต้องการทราบว่าฉันควรเขียนส่วนต่อไปของรหัสอย่างไร
Guillaume


3
@antlersoft: ฟังก์ชั่น 2,000 บรรทัดที่ทำรายการสิ่งที่เรียบง่ายซึ่งต่อเนื่องกันมานานไม่มีปัญหาและไม่ต้องมีข้อแก้ตัว โปรดอย่าตอบกลับด้วยข้อโต้แย้งแบบวงกลมเช่น "คุณไม่ควรเขียนฟังก์ชัน 2,000 บรรทัดเพราะคุณไม่ควรเขียนฟังก์ชัน 2,000 บรรทัด"
gnasher729

คำตอบ:


14

ฉันสามารถอ่านจากคำถามของคุณปัญหาไม่ใช่ว่ารหัสเก่า C แต่โปรแกรมไม่ดี ปัญหาส่วนใหญ่ที่คุณกล่าวถึงเช่นการใช้คำฟุ่มเฟื่อยฟังก์ชั่นสายใหญ่กว่า 2,000+ หรือไม่มีการแยกข้อกังวลเกี่ยวข้องกับภาษาใด ๆ C หรือ Java

การพูดอย่างละเอียดถูกกล่าวถึงในบริบทของการจัดการข้อผิดพลาด คุณไม่ได้ให้ตัวอย่างดังนั้นฉันสามารถเตือนได้ว่ารหัสการจัดการข้อผิดพลาดเป็นรหัสเท่านั้น ไม่มีข้อแก้ตัวสำหรับส่วนซ้ำ ๆ ของรหัสสำเร็จรูป แยกมันออก; ไม่ว่าจะเป็นฟังก์ชั่นหรือ (ถ้ามันไม่คุ้มค่าที่จะสร้างฟังก์ชั่นแยก) ทำgoto Error;รูปแบบและย้ายการจัดการข้อผิดพลาดและการล้างทรัพยากรไปยังError:ส่วนที่ด้านล่างของฟังก์ชั่น

ถ้าผ่านข้อผิดพลาดขึ้นห่วงโซ่การโทรที่ดูเหมือนว่าจะเป็นปัญหาให้ถามตัวเอง: ไม่ฟังก์ชั่นขึ้นที่นั่นจริงๆจำเป็นต้องรู้เล็ก ๆ น้อย ๆ บางคนลงที่นี่มีปัญหาหรือไม่ กลไกการยกเว้นในภาษาทำให้การทำเช่นนั้นเป็นเรื่องง่าย แต่โดยทั่วไปจะดีกว่าที่จะจัดการกับข้อยกเว้นก่อนหน้า (ในภาษาใด ๆ ) เพื่อให้เงื่อนไขข้อผิดพลาดไม่ก่อให้เกิดตรรกะของรหัสระดับสูง และถ้าฟังก์ชั่นขึ้นที่นั่นจริงๆต้องการที่จะรู้ว่ามีวิธีการที่จะยกเว้นเลียนแบบด้วยและsetjmplongjmp

ฉันคิดว่าปัญหาที่เกี่ยวข้องกับ C เท่านั้นที่กล่าวถึงคือการขาดตู้คอนเทนเนอร์มาตรฐาน ในขณะที่Setโดยทั่วไปจะถูกแทนที่ด้วยอาร์เรย์ที่เรียงและMap(ส่วนใหญ่) ด้วยอาร์เรย์ของคู่หรือstruct(ถ้าคุณรู้ว่าชุดคีย์ก่อนที่มือmap[key] = valueจะกลายเป็นs.key = value) แต่ความจริงก็คือไม่มีภาชนะอาร์เรย์แบบไดนามิกในมาตรฐาน ห้องสมุด. ใน C99 อย่างน้อยคุณสามารถประกาศอาเรย์ความยาวแปรผันในสแต็ก ( int array[len]) แต่คุณต้องคำนวณlenล่วงหน้า (โดยทั่วไปจะไม่ยาก) และแน่นอนว่าคุณไม่สามารถส่งคืนมันเป็นวัตถุที่จัดสรรสแต็กได้ โครงการส่วนใหญ่จบลงด้วยการเขียนคอนเทนเนอร์อาเรย์แบบไดนามิกของตัวเองหรือใช้โอเพ่นซอร์ส

ในบันทึกปิดฉันต้องการชี้ให้เห็นว่าฉันเคยไปที่นั่นมาแล้ว ฉันเป็นโปรแกรมเมอร์ Java ที่ย้ายไปยัง C ++ และบริสุทธิ์ C. ฉันอยากจะแนะนำ "อ่านหนังสือ X เพื่อเรียนรู้ C ที่ดี" แต่ไม่มีอะไรเหมือนที่ไม่มีสำหรับ Java วิธีที่จะก้าวไปข้างหน้าคือการดื่มด่ำกับภาษาและภาษาของห้องสมุดมาตรฐาน google อ่านเยอะมากและเขียนโค๊ตจนกระทั่งคุณเริ่มคิดใน C. การพยายามเขียนสิ่งต่าง ๆ ใน C อย่างที่คุณทำใน Java นั้นน่าผิดหวังเหมือนกับการพยายามเขียนประโยคเป็นภาษาต่างประเทศด้วยคำที่แปลโดยตรงจากแม่ของคุณ ลิ้น; ทั้งคุณและผู้อ่านจะประจบประแจง ข่าวดีก็คือการเรียนรู้การเขียนโปรแกรมที่ดีช้า แต่การเรียนรู้ภาษาอื่นนั้นรวดเร็ว ดังนั้นถ้าคุณเขียนโค้ดที่เหมาะสมใน Java


1
สรุปแล้วนี่เป็นคำตอบที่ดีจริงๆ ฉันแค่อยากจะเห็นsetjmp()/ longjmp()เป็นเครื่องมือที่ถูกต้อง: มันไม่ได้พยายามทำการล้างข้อมูลใด ๆ การจัดสรรใด ๆ จะรั่วไหลการล็อคใด ๆ ที่ค้างไว้จะไม่ถูกปล่อยออกมาไฟล์ใด ๆ ที่เปิดอยู่จะไม่ถูกปิดและความไม่สอดคล้องกันของข้อมูลชั่วคราวจะกลายเป็นถาวร IMHO ฟังก์ชั่นนี้เป็นคู่ที่แฮ็คที่แย่ที่สุดที่เคยคิดค้นมาโดยมีเหตุผลเพียงอย่างเดียวว่ามันเป็นไปได้ที่จะใช้มัน ในท้ายที่สุดมีวิธีที่ถูกต้องเพียงวิธีเดียวเท่านั้นในการจัดการข้อผิดพลาดใน C: รหัสข้อผิดพลาดที่ชัดเจน
cmaster - คืนสถานะโมนิกา

@cmaster ใช่ โดยส่วนตัวแล้วsetjmp/longjmpดูเหมือนปลาในน้ำใน C และฉันไม่เคยใช้มัน ฉันรู้สึกว่าถูกบังคับให้รวมพวกเขาเพียงเพราะบทเรียน / ห้องสมุดจำนวนมากบนอินเทอร์เน็ตสำหรับการเลียนแบบข้อยกเว้นดังนั้นฉันคิดว่ามีคนที่ใช้งานจริง
นกฮูก

7

ข้อดีของโครงการคือรหัสตรงไปตรงมา: ไม่มีเฟรมเวิร์กไม่มีการจัดการโค้ดไบต์ที่รันไทม์ไม่มี AOP และเซิร์ฟเวอร์สามารถตอบผู้ใช้มากกว่า 10,000 คนพร้อมกันด้วยเครื่องเดียวโดยใช้หน่วยความจำน้อยกว่าจาวาต้องคาย "hello world"

ฉันขอแนะนำให้คุณระมัดระวังว่าสิ่งนี้คุ้มค่ากับเวลาของคุณและเงินของ บริษัท ในการใช้ทรัพยากรใน "modernizing" ซอฟต์แวร์ที่ใช้งานได้ซึ่งมีความซับซ้อนของรหัสต่ำและทำงานได้ดี มีฮูดที่มีแนวโน้มสูงที่คุณจะแนะนำบั๊กใหม่ด้วยตัวเองโดยเฉพาะอย่างยิ่งเนื่องจากดูเหมือนว่าเป็นระบบที่คุณไม่คุ้นเคย

หากคุณยังต้องการลงเส้นทางนั้นฉันจะแนะนำต่อไปนี้:

  • สร้าง (หรือสร้าง) ไดอะแกรมสถานะของซอฟต์แวร์ / รหัส
  • ดำดิ่งลงไปในรหัสและทำรายการส่วนที่ซับซ้อนที่สุดหรือสำคัญที่สุดของรหัสตามลำดับ
  • ค้นหาใครบางคนที่มีความรู้เกี่ยวกับฐานรหัสนั้นและถามพวกเขาว่าทำไมมันถึงถูกสร้างขึ้นด้วยวิธีนี้และสิ่งที่เป็นที่รู้กันว่าทำให้เกิดปัญหา
  • เขียนเอกสารจากสิ่งที่คุณได้เรียนรู้

เมื่อมาถึงจุดนี้คุณจะตัดสินใจว่าคุ้มค่าที่จะสำรวจหรือไม่ หากวัฒนธรรมของ บริษัท ของคุณไม่ให้รางวัลแก่ความล้มเหลวก็ให้เปิดไฟเขียวขึ้นหรือขึ้นจากผู้จัดการ

  • แบ่งหน่วยการสร้างที่แตกต่างกันของซอฟต์แวร์และเขียนหน่วยทดสอบสำหรับแต่ละหน่วย
  • ทำซ้ำจนกว่าคุณจะสามารถกาวโมดูลที่แตกต่างกันเข้าด้วยกัน
  • ทำการทดสอบเพิ่มเติมที่จำลองการโต้ตอบกับผู้ใช้จริง (การทดสอบความเครียดเป็นต้น)

ฉันคิดว่ามันเป็นแผนที่ถนนที่ค่อนข้างดีและพาคุณไปทุกที่ที่คุณต้องการ โดยไม่ทราบลักษณะเฉพาะของโครงการนี้มันเป็นเรื่องยากที่จะช่วยคุณได้มาก โปรดอย่าทิ้งข้อจำกัดความรับผิดชอบของฉันเป็นคนตื่นตกใจมากเกินไป โปรแกรมเมอร์ที่ยอดเยี่ยมมากมายได้เอาชนะฝุ่นละอองด้วยการพยายามเขียนโครงการที่มีอยู่เป็นภาษาที่พวกเขาชื่นชอบหรือใช้เครื่องมือ "ทันสมัย" นั่นคือการตัดสินใจที่จะต้องคิดออกมาอย่างรอบคอบและฉันขอแนะนำให้คุณอย่าโกงและทำสิ่งนั้นด้วยตัวคุณเองโดยไม่ได้รับการสนับสนุนด้านการจัดการหรือความช่วยเหลือจากเพื่อนร่วมงานของคุณ


2
ฉันกำหนดใหม่อีกครั้งว่าคำถามของฉันยังไม่ชัดเจน ฉันไม่ต้องการ refactor รหัส เลย ฉันต้องการรักษารหัสฐานที่มีอยู่ในแบบที่เป็นอยู่ อย่างไรก็ตามฉันต้องการเรียนรู้วิธีการเขียน modern C สำหรับคุณสมบัติใหม่ และที่นี่ฉันหลงทาง เอกสารส่วนใหญ่ที่ฉันพบคือเกี่ยวกับวิธีการเขียนโค้ดใน C ไม่ใช่เกี่ยวกับวิธีการเขียน 'ทันสมัย' C. อาจไม่มีสิ่งเช่น 'ทันสมัย' C ...
Guillaume

1

หากคุณต้องการภาษาระดับสูงกว่ามีบางภาษาเช่น C ++ หรือ Objective-C ที่สามารถผสมกับรหัส C ได้อย่างง่ายดาย

อีกวิธีหนึ่ง C และ C ++ เข้ากันได้อย่างสมเหตุสมผล คุณอาจจะสามารถรวบรวม codebase ทั้งหมดเป็น C ++ โดยมีการเปลี่ยนแปลงเล็กน้อยคุณจะมีตัวแปรเป็นครั้งคราวที่ชื่อว่า "class" หรือ "template" ที่คุณต้องเปลี่ยนชื่อ แต่ในทางปฏิบัติจะเป็นเช่นนั้นทั้งหมด (sizeof ('a') จะแตกต่างกันใน C และ C ++ แต่ฉันไม่คิดว่าฉันเคยใช้มันมาก่อน)

หากคุณไปเส้นทางนั้นให้พิจารณาว่าผู้ดูแลคนต่อไปอาจไม่คล่องแคล่วเกินไปกับ C ++ อย่าถูกพาตัวไป ใช้ประโยชน์จาก C ++ แต่จนถึงตอนนี้โปรแกรมเมอร์ C สามารถเข้าใจได้ง่าย


1
ฉันต้องไม่เห็นด้วยที่นี่ C และ C ++ เป็นภาษาที่แตกต่างกันและบางรหัสที่ต้องการโดยคอมไพเลอร์ C ++ (การส่งคืนค่าอย่างชัดเจนmalloc) ถือเป็นแนวปฏิบัติที่ไม่ดีใน C. ความหมายของconstและinlineยังแตกต่างกันมากระหว่าง C และ C ++ และแน่นอน C ++ ไม่เข้าใจ__restrict. อย่าถือว่าภาษานั้นใช้แทนกันได้แม้ในแหล่งข้อมูลย่อยที่รวบรวมทั้งสองอย่าง
Angew ไม่ภูมิใจใน SO

1

โดยทั่วไปการเขียนโค้ดดีซีเป็นเพียงเช่นเดียวกับการเขียนที่ดี C ++ หรือ Java รหัส: structคุณต้องการระดับการใช้งาน คุณต้องการรับมรดกรวมฐานstructเป็นสมาชิกแรกนิรนาม คุณต้องการฟังก์ชั่นเสมือนจริงเพิ่มตัวชี้ไปยังตัวชี้structฟังก์ชั่นคงที่ และอื่น ๆ เป็นต้นมันคือสิ่งที่ C ++ ทำภายใต้ประทุนความแตกต่างเพียงอย่างเดียวคือมันชัดเจนใน C ดังนั้นคุณสามารถเขียนโปรแกรมเชิงวัตถุใน C ได้อย่างสมบูรณ์แบบมันดูแตกต่างและแตกต่างจากที่คุณ จะใช้ในการ

ประเด็นคือการเขียนโปรแกรมที่ดีนั้นเกี่ยวกับกระบวนทัศน์ไม่ใช่เกี่ยวกับคุณสมบัติทางภาษา จริงมันดีเสมอถ้าคุณสมบัติภาษาของคุณให้การสนับสนุนที่ดีสำหรับกระบวนทัศน์ที่คุณต้องการใช้ แต่คุณสมบัติภาษานั้นไม่ใช่ข้อกำหนด เมื่อคุณตระหนักถึงสิ่งนี้คุณสามารถเขียนโค้ดที่ดีในภาษาใดก็ได้ (นอกเหนือจากภาษาลึกลับบางอย่างเช่น brainfuck หรือ INTERCAL นั่นคือ)

แน่นอนปัญหายังคงอยู่ที่ไลบรารี C มาตรฐานไม่มีคลาสคอนเทนเนอร์ที่ดีที่คุณคุ้นเคย แต่น่าเสียดายที่นั่นหมายความว่าคุณจะต้องใช้ของคุณเองหรือแก้ไขการขาดนี้โดยใช้อาร์เรย์ที่จัดสรรแบบไดนามิก แต่ฉันจะพนันในไม่ช้าคุณจะพบว่าสิ่งที่คุณต้องการจริงๆคืออาร์เรย์แบบไดนามิก ( malloc()) และรายการ / ต้นไม้ที่เชื่อมโยงซึ่งดำเนินการผ่านสมาชิกตัวชี้ภายในชั้นเรียนของคุณ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.