แก้ไขข้อผิดพลาดการแบ่งกลุ่มใน C ++


95

ฉันกำลังเขียนโปรแกรม C ++ ข้ามแพลตฟอร์มสำหรับ Windows และ Unix ทางฝั่ง Window โค้ดจะคอมไพล์และรันได้อย่างไม่มีปัญหา ในด้าน Unix จะรวบรวมอย่างไรก็ตามเมื่อฉันพยายามเรียกใช้ฉันได้รับข้อผิดพลาดในการแบ่งส่วน ลางสังหรณ์เริ่มต้นของฉันคือมีปัญหากับพอยน์เตอร์

อะไรคือวิธีการที่ดีในการค้นหาและแก้ไขข้อผิดพลาดในการแบ่งส่วน

คำตอบ:


135
  1. คอมไพล์แอปพลิเคชันของคุณด้วย-gจากนั้นคุณจะมีสัญลักษณ์ดีบักในไฟล์ไบนารี

  2. ใช้gdbเพื่อเปิดคอนโซล gdb

  3. ใช้fileและส่งไฟล์ไบนารีของแอปพลิเคชันของคุณในคอนโซล

  4. ใช้runและส่งผ่านข้อโต้แย้งใด ๆ ที่แอปพลิเคชันของคุณต้องการเพื่อเริ่มต้น

  5. ทำบางอย่างเพื่อทำให้เกิดความผิดพลาดในการแบ่งกลุ่ม

  6. พิมพ์btในgdbคอนโซลที่จะได้รับการติดตามสแต็คของการแบ่งส่วนความผิดพลาด


การรวบรวมgในบริบทหมายความว่าCMakeอย่างไร
Schütze

2
เปิดใช้งานประเภทบิวด์การดีบัก วิธีหนึ่งคือcmake -DCMAKE_BUILD_TYPE=Debug.
Antonin Décimo

36

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


19

ก่อนที่ปัญหาจะเกิดขึ้นพยายามหลีกเลี่ยงปัญหานี้ให้มากที่สุด:

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

ใช้เครื่องมือที่เหมาะสมสำหรับการดีบัก บน Unix:

  • GDBสามารถบอกคุณได้ว่าคุณโปรแกรมขัดข้องตรงไหนและจะให้คุณเห็นในบริบทใด
  • Valgrindจะช่วยคุณตรวจจับข้อผิดพลาดเกี่ยวกับหน่วยความจำมากมาย
  • กับ GCC คุณยังสามารถใช้Mudflapกับ GCC, เสียงดังกราวและตั้งแต่เดือนตุลาคมทดลองMSVCคุณสามารถใช้ที่อยู่ / หน่วยความจำฆ่าเชื้อ สามารถตรวจจับข้อผิดพลาดบางอย่างที่ Valgrind ไม่ทำและการสูญเสียประสิทธิภาพจะเบาลง ใช้โดยการคอมไพล์กับ-fsanitize=addressแฟล็ก

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


5

ใน Unix คุณสามารถใช้valgrindเพื่อค้นหาปัญหา ฟรีและมีประสิทธิภาพ หากคุณต้องการทำด้วยตัวเองคุณสามารถโอเวอร์โหลดตัวดำเนินการnewและdeleteเพื่อตั้งค่าการกำหนดค่าโดยที่คุณมี 1 ไบต์0xDEADBEEFก่อนและหลังวัตถุใหม่แต่ละชิ้น จากนั้นติดตามสิ่งที่เกิดขึ้นในการทำซ้ำแต่ละครั้ง สิ่งนี้อาจล้มเหลวในการจับทุกอย่าง (คุณไม่รับประกันว่าจะสัมผัสไบต์เหล่านั้นด้วยซ้ำ) แต่ที่ผ่านมามันได้ผลสำหรับฉันบนแพลตฟอร์ม Windows


1
นี่จะเป็น 4 ไบต์แทนที่จะเป็น 1 ... แต่หลักการก็ดี
Jonas Wagner

1
ฉันอาจเชื่อมโยงไปของฉันดีบักกองไม่ล่วงล้ำ ? :-)
fredoverflow

ไปเลย เราทุกคนกำลังช่วยเหลือผู้อื่นที่นี่ดังนั้นควรเพิ่มสิ่งที่สามารถช่วยได้
วีทตี้

แม้ว่าจะทำงานหนักเกินไปnewและdeleteมีประโยชน์อย่างมาก แต่การใช้-fsanitize=addressเป็นตัวเลือกที่ดีกว่าเนื่องจากคอมไพเลอร์จะคอมไพล์ในการตรวจหารันไทม์สำหรับปัญหาและจะถ่ายโอนหน่วยความจำไปที่หน้าจอโดยอัตโนมัติซึ่งทำให้วิธีการดีบักง่ายขึ้น
Tarick Welling

3

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

เพื่อหลีกเลี่ยงไม่ให้พอยน์เตอร์ที่ไม่ได้กำหนดค่าเริ่มต้นเป็นตัวแปรท้องถิ่นให้ลองประกาศให้ช้าที่สุดเท่าที่จะทำได้โดยเฉพาะอย่างยิ่ง (ซึ่งเป็นไปไม่ได้เสมอไป) เมื่อสามารถเริ่มต้นด้วยค่าที่มีความหมายได้ โน้มน้าวตัวเองว่าพวกเขาจะมีค่าก่อนที่จะถูกใช้โดยการตรวจสอบโค้ด หากคุณมีปัญหาให้เริ่มต้นให้เป็นค่าคงที่ของตัวชี้ที่เป็นโมฆะ (โดยปกติจะเขียนเป็นNULLหรือ0) แล้วตรวจสอบ

เพื่อหลีกเลี่ยงตัวชี้ที่ไม่ได้กำหนดค่าเริ่มต้นเป็นค่าสมาชิกตรวจสอบให้แน่ใจว่าได้เริ่มต้นอย่างถูกต้องในตัวสร้างและจัดการอย่างถูกต้องในตัวสร้างสำเนาและตัวดำเนินการกำหนด อย่าพึ่งพาinitฟังก์ชันสำหรับการจัดการหน่วยความจำแม้ว่าคุณจะสามารถเริ่มต้นอื่น ๆ ได้

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

ใช้ตัวชี้อัจฉริยะเมื่อมี ข้อได้เปรียบที่สำคัญคือหากคุณยึดติดและใช้มันอย่างสม่ำเสมอคุณสามารถหลีกเลี่ยงการเขียนได้อย่างสมบูรณ์deleteและจะไม่มีการลบซ้ำ

ใช้สตริง C ++ และคลาสคอนเทนเนอร์ทุกครั้งที่ทำได้แทนสตริงและอาร์เรย์สไตล์ C พิจารณาใช้.at(i)มากกว่า[i]เพราะจะบังคับให้มีการตรวจสอบขอบเขต ดูว่าคอมไพลเลอร์หรือไลบรารีของคุณสามารถตั้งค่าให้ตรวจสอบขอบเขตได้[i]หรือไม่อย่างน้อยก็ในโหมดดีบัก ความผิดพลาดในการแบ่งกลุ่มอาจเกิดจากการโอเวอร์รันบัฟเฟอร์ที่เขียนขยะทับตัวชี้ที่ดีอย่างสมบูรณ์แบบ

การทำสิ่งเหล่านั้นจะช่วยลดโอกาสในการเกิดข้อผิดพลาดในการแบ่งส่วนและปัญหาหน่วยความจำอื่น ๆ ได้มาก พวกเขาจะล้มเหลวในการแก้ไขทุกอย่างอย่างไม่ต้องสงสัยและนั่นคือเหตุผลที่คุณควรใช้ valgrind ในตอนนี้เมื่อคุณไม่มีปัญหาและ valgrind และ gdb เมื่อคุณทำ


1

ฉันไม่รู้วิธีการใด ๆ ที่จะใช้เพื่อแก้ไขสิ่งต่างๆเช่นนี้ ฉันไม่คิดว่าจะเป็นไปได้ที่จะเกิดขึ้นอย่างใดอย่างหนึ่งสำหรับปัญหาที่เกิดขึ้นคือพฤติกรรมของโปรแกรมของคุณไม่ได้กำหนดไว้ (ฉันไม่รู้ว่าในกรณีใดเมื่อ SEGFAULT ไม่ได้เกิดจาก UB บางประเภท) .

มี "วิธีการ" ทุกประเภทเพื่อหลีกเลี่ยงปัญหาก่อนที่จะเกิดขึ้น หนึ่งที่สำคัญคือ RAII

นอกจากนั้นคุณต้องทุ่มพลังจิตที่ดีที่สุดให้กับมัน

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