ผิดส่วนเกิดขึ้นเมื่อโปรแกรมพยายามที่จะเข้าถึงหน่วยความจำภายนอกของพื้นที่ที่ได้รับการจัดสรรสำหรับมัน
ในกรณีนี้โปรแกรมเมอร์ C ที่มีประสบการณ์จะเห็นว่าปัญหาเกิดขึ้นในบรรทัดที่sprintf
เรียกว่า แต่ถ้าคุณไม่สามารถบอกได้ว่าผิดส่วนของคุณจะเกิดขึ้นหรือถ้าคุณไม่ต้องการที่จะรำคาญการอ่านผ่านรหัสเพื่อพยายามที่จะคิดออกแล้วคุณสามารถสร้างโปรแกรมของคุณที่มีสัญลักษณ์การแก้ปัญหา (มีgcc
ที่-g
ธงไม่นี้ ) จากนั้นเรียกใช้ผ่านตัวดีบัก
slope.c
ผมคัดลอกรหัสต้นฉบับของคุณและวางมันลงไปฉันไฟล์ชื่อ จากนั้นฉันก็สร้างมันขึ้นมาเช่นนี้
gcc -Wall -g -o slope slope.c
( -Wall
ตัวเลือกนี้เป็นเพียงการทำให้คำเตือนสำหรับสถานการณ์เพิ่มเติมเท่านั้นซึ่งสามารถช่วยในการหาสิ่งที่อาจผิดเช่นกัน)
จากนั้นฉันก็รันโปรแกรมในตัวดีบั๊กเกอร์gdb
โดยการรันครั้งแรกgdb ./slope
เพื่อเริ่มต้นgdb
ด้วยโปรแกรมจากนั้นหนึ่งครั้งในตัวดีบักให้run
คำสั่งกับตัวดีบัก:
ek@Kip:~/source$ gdb ./slope
GNU gdb (GDB) 7.5-ubuntu
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/ek/source/slope...done.
(gdb) run
Starting program: /home/ek/source/slope
warning: Cannot call inferior functions, you have broken Linux kernel i386 NX (non-executable pages) support!
Program received signal SIGSEGV, Segmentation fault.
0x001a64cc in _IO_default_xsputn () from /lib/i386-linux-gnu/libc.so.6
(ไม่ต้องกังวลกับข้อความyou have broken Linux kernel i386 NX
... ของฉันsupport
มันไม่ได้ป้องกันgdb
การใช้งานอย่างมีประสิทธิภาพในการดีบักโปรแกรมนี้)
ข้อมูลที่เป็นความลับอย่างมาก ... และถ้าคุณไม่ได้มีสัญลักษณ์การแก้ปัญหาการติดตั้งสำหรับ libc _IO_default_xsputn
แล้วคุณจะได้รับข้อความแม้คลุมเครือมากขึ้นว่ามีที่อยู่เลขฐานสิบหกแทนชื่อฟังก์ชันสัญลักษณ์ โชคดีที่มันไม่สำคัญเพราะสิ่งที่เราอยากรู้คือปัญหาที่เกิดขึ้นในโปรแกรมของคุณ
ดังนั้นวิธีแก้ไขคือดูย้อนหลังเพื่อดูว่ามีการเรียกใช้ฟังก์ชันใดที่นำไปสู่การเรียกใช้ฟังก์ชั่นนั้นในไลบรารีระบบที่SIGSEGV
สัญญาณถูกทริกเกอร์ในที่สุด
gdb
(และดีบักใด ๆ ) มีคุณลักษณะนี้สร้างขึ้นใน: ก็เรียกว่ากองติดตามหรือการติดตามย้อนหลัง ฉันใช้bt
คำสั่ง debugger เพื่อสร้าง backtrace ในgdb
:
(gdb) bt
#0 0x001a64cc in _IO_default_xsputn () from /lib/i386-linux-gnu/libc.so.6
#1 0x00178e04 in vfprintf () from /lib/i386-linux-gnu/libc.so.6
#2 0x0019b234 in vsprintf () from /lib/i386-linux-gnu/libc.so.6
#3 0x0017ff7b in sprintf () from /lib/i386-linux-gnu/libc.so.6
#4 0x080484cc in calc_slope (input1=1, input2=150) at slope.c:26
#5 0x08048578 in main () at slope.c:52
(gdb)
คุณสามารถเห็นได้ว่าmain
ฟังก์ชั่นของคุณเรียกใช้calc_slope
ฟังก์ชั่น (ซึ่งคุณตั้งใจ) แล้วcalc_slope
เรียกใช้sprintf
ซึ่ง (ในระบบนี้) นำไปใช้กับการเรียกไปยังฟังก์ชั่นห้องสมุดอื่น ๆ ที่เกี่ยวข้อง
สิ่งที่คุณกำลังสนใจทั่วไปในคือการเรียกใช้ฟังก์ชันในโปรแกรมของคุณที่เรียกฟังก์ชั่นด้านนอกของโปรแกรมของคุณ หากไม่มีข้อผิดพลาดในไลบรารี / ไลบรารีเองที่คุณใช้ (ในกรณีนี้ไลบรารี C มาตรฐานที่libc
จัดเตรียมโดยไฟล์ไลบรารีlibc.so.6
) ข้อผิดพลาดที่ทำให้เกิดความผิดพลาดนั้นอยู่ในโปรแกรมของคุณและมักจะอยู่ที่หรือใกล้กับ การโทรครั้งสุดท้ายในโปรแกรมของคุณ
ในกรณีนี้นั่นคือ:
#4 0x080484cc in calc_slope (input1=1, input2=150) at slope.c:26
sprintf
นั่นคือสิ่งที่โทรโปรแกรมของคุณ เรารู้สิ่งนี้เพราะsprintf
เป็นขั้นตอนต่อไป แต่ถึงกระนั้นก็ไม่ได้ระบุว่าคุณรู้เรื่องนี้เพราะนั่นคือสิ่งที่เกิดขึ้นในบรรทัดที่ 26และมันบอกว่า:
... at slope.c:26
ในโปรแกรมของคุณบรรทัด 26 ประกอบด้วย:
sprintf(s,"%d",curr);
(คุณควรใช้โปรแกรมแก้ไขข้อความที่แสดงหมายเลขบรรทัดโดยอัตโนมัติอย่างน้อยที่สุดสำหรับบรรทัดที่คุณกำลังใช้อยู่ซึ่งเป็นประโยชน์อย่างมากในการตีความข้อผิดพลาดทั้งในเวลาคอมไพล์
ตามที่กล่าวไว้ในคำตอบที่เดนนิส Kaarsemaker ของ , s
เป็นอาร์เรย์หนึ่งไบต์ (ไม่เป็นศูนย์เนื่องจากค่าที่คุณกำหนดให้""
มีความยาวหนึ่งไบต์นั่นคือมันจะเท่ากับ{ '\0' }
ในวิธีเดียวกันกับที่"Hello, world!\n"
เท่ากับ{ 'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n', '\0' }
)
ดังนั้นจึงอาจจะยังคงทำงานนี้บนแพลตฟอร์มบาง (และเห็นได้ชัดว่าเมื่อรวบรวมกับ vc9 สำหรับ Windows)?
คนมักจะพูดว่าเมื่อคุณจัดสรรหน่วยความจำแล้วลองเข้าถึงหน่วยความจำภายนอกมันจะสร้างข้อผิดพลาด แต่นั่นไม่จริงเลย ตามมาตรฐานทางเทคนิค C และ C ++ สิ่งที่สิ่งนี้สร้างขึ้นจริงๆคือพฤติกรรมที่ไม่ได้กำหนด
ในคำอื่น ๆ สิ่งที่สามารถเกิดขึ้นได้!
ถึงกระนั้นบางสิ่งก็มีแนวโน้มมากกว่าสิ่งอื่น เหตุใดอาร์เรย์ขนาดเล็กบนสแต็กจะปรากฏขึ้นในการใช้งานบางอย่างเพื่อให้ทำงานเหมือนอาร์เรย์ขนาดใหญ่บนสแต็ก
สิ่งนี้เกิดขึ้นกับวิธีการนำการจัดสรรสแต็กมาใช้ซึ่งได้รับอนุญาตให้เปลี่ยนจากแพลตฟอร์มหนึ่งไปอีกแพลตฟอร์มหนึ่ง ความสามารถในการปฏิบัติการของคุณอาจจัดสรรหน่วยความจำให้กับสแต็กมากกว่าที่ตั้งใจจะใช้ในแต่ละครั้ง บางครั้งสิ่งนี้อาจทำให้คุณสามารถเขียนไปยังตำแหน่งหน่วยความจำที่คุณไม่ได้อ้างสิทธิ์ไว้อย่างชัดเจนในรหัสของคุณ เป็นไปได้มากว่านี่คือสิ่งที่เกิดขึ้นเมื่อคุณสร้างโปรแกรมใน VC9
อย่างไรก็ตามคุณไม่ควรพึ่งพาพฤติกรรมนี้แม้ใน VC9 มันอาจขึ้นอยู่กับไลบรารีเวอร์ชันต่าง ๆ ที่อาจมีอยู่ในระบบ Windows ที่แตกต่างกัน แต่มีโอกาสมากขึ้นที่จะเกิดปัญหาว่าการจัดสรรพื้นที่สแต็กเพิ่มเติมด้วยความตั้งใจที่จะใช้จริงและอาจใช้งานได้จริงจากนั้นคุณจะพบกับฝันร้ายเต็มรูปแบบของ "พฤติกรรมที่ไม่ได้กำหนด" ซึ่งในกรณีนี้มากกว่าหนึ่งตัวแปรสามารถจบลงด้วยการเก็บไว้ในที่เดียวกันโดยที่การเขียนไปที่หนึ่งเขียนทับอีก ... แต่ไม่เสมอไปเพราะบางครั้งเขียนถึงตัวแปร ถูกแคชในรีจิสเตอร์และไม่ได้ดำเนินการจริงทันที (หรืออ่านตัวแปรอาจถูกแคชหรือตัวแปรอาจสันนิษฐานว่าเป็นเหมือนเดิมมาก่อนเพราะหน่วยความจำที่จัดสรรให้มันเป็นที่รู้จักกันโดยคอมไพเลอร์ที่ไม่ได้เขียนถึง ตัวแปรเอง)
และนั่นทำให้ฉันมีความเป็นไปได้อื่น ๆ ว่าทำไมโปรแกรมทำงานเมื่อสร้างด้วย VC9 มันเป็นไปได้และมีแนวโน้มที่ค่อนข้างที่บางอาร์เรย์หรืออื่น ๆ ตัวแปรถูกจัดสรรจริงโดยโปรแกรมของคุณ (ซึ่งอาจรวมถึงการได้รับการจัดสรรโดยห้องสมุดโปรแกรมของคุณใช้) s
เพื่อใช้ช่องว่างหลังจากอาร์เรย์หนึ่งไบต์ ดังนั้นการs
ถือว่าเป็นอาร์เรย์ที่มีความยาวมากกว่าหนึ่งไบต์จะมีผลในการเข้าถึงเนื้อหาของตัวแปรนั้น / อาเรย์เหล่านั้นซึ่งอาจไม่ดีเช่นกัน
สรุปได้ว่าเมื่อคุณมีความผิดพลาดเช่นนี้ก็โชคดีที่จะได้รับข้อผิดพลาดเช่น "ความผิดส่วน" หรือ "การป้องกันความผิดทั่วไป." เมื่อคุณไม่มีสิ่งนั้นคุณอาจไม่ทราบจนกว่าจะสายเกินไปที่โปรแกรมของคุณจะมีพฤติกรรมที่ไม่ได้กำหนด