พฤติกรรมที่ไม่ได้กำหนด

จากวิกิพีเดีย สารานุกรมเสรี
ข้ามไปที่การนำทาง ข้ามไปที่การค้นหา

ในการเขียนโปรแกรมคอมพิวเตอร์ , พฤติกรรมที่ไม่ได้กำหนด ( UB ) เป็นผลมาจากการดำเนินโครงการที่มีพฤติกรรมที่มีการกำหนดที่จะคาดเดาไม่ได้ในสเปคภาษาซึ่งรหัสคอมพิวเตอร์ปฏิบัติตาม ซึ่งแตกต่างจากพฤติกรรมที่ไม่ระบุซึ่งข้อกำหนดภาษาไม่ได้กำหนดผลลัพธ์ และพฤติกรรมที่กำหนดไว้ในการดำเนินการที่เลื่อนไปจากเอกสารประกอบของส่วนประกอบอื่นของแพลตฟอร์ม (เช่นABIหรือเอกสารสำหรับนักแปล )

ในชุมชน Cพฤติกรรมที่ไม่ได้กำหนดอาจเรียกว่า " ปีศาจจมูก " อย่างตลกขบขันหลังจากโพสต์comp.std.cที่อธิบายพฤติกรรมที่ไม่ได้กำหนดไว้ว่าอนุญาตให้คอมไพเลอร์ทำทุกอย่างที่เลือก แม้กระทั่ง "เพื่อให้ปีศาจบินออกจากจมูกของคุณ ". [1]

ภาพรวม

บางคนเขียนโปรแกรมภาษาอนุญาตให้โปรแกรมในการทำงานที่แตกต่างกันหรือแม้กระทั่งมีการควบคุมการไหลที่แตกต่างจากรหัสที่มาตราบเท่าที่มันจัดแสดงนิทรรศการผู้ใช้มองเห็นเหมือนกันผลข้างเคียง , ถ้าไม่ได้กำหนดพฤติกรรมที่ไม่เคยเกิดขึ้นในระหว่างการทำงานของโปรแกรมพฤติกรรมที่ไม่ได้กำหนดคือชื่อของรายการเงื่อนไขที่โปรแกรมต้องไม่ปฏิบัติตาม

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

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

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

สำหรับ C และ C++ คอมไพเลอร์ได้รับอนุญาตให้ทำการวินิจฉัยเวลาคอมไพล์ในกรณีเหล่านี้ แต่ไม่จำเป็นสำหรับ: การนำไปใช้จะถือว่าถูกต้องไม่ว่าในกรณีใด ๆ ก็ตามที่ทำในกรณีดังกล่าว คล้ายกับคำศัพท์ที่ไม่ใส่ใจในตรรกะดิจิทัล . เป็นความรับผิดชอบของโปรแกรมเมอร์ในการเขียนโค้ดที่ไม่เคยเรียกใช้พฤติกรรมที่ไม่ได้กำหนด แม้ว่าการใช้งานคอมไพเลอร์จะได้รับอนุญาตให้ออกการวินิจฉัยเมื่อเกิดเหตุการณ์นี้ขึ้น คอมไพเลอร์ในปัจจุบันมีธงที่ช่วยให้การวินิจฉัยดังกล่าวเช่น-fsanitizeช่วยให้ "พฤติกรรมเจลทำความสะอาดไม่ได้กำหนด" ( UBSan ) ในGCC 4.9 [2]และในเสียงดังกราวอย่างไรก็ตาม แฟล็กนี้ไม่ใช่ค่าดีฟอลต์ และการเปิดใช้งานเป็นทางเลือกของผู้ที่สร้างโค้ด

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

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

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

ประโยชน์ที่ได้รับ

การบันทึกการดำเนินการเป็นพฤติกรรมที่ไม่ได้กำหนดไว้ช่วยให้คอมไพเลอร์สามารถสันนิษฐานได้ว่าการดำเนินการนี้จะไม่เกิดขึ้นในโปรแกรมที่สอดคล้อง สิ่งนี้ทำให้คอมไพเลอร์มีข้อมูลเพิ่มเติมเกี่ยวกับโค้ด และข้อมูลนี้สามารถนำไปสู่โอกาสในการเพิ่มประสิทธิภาพที่มากขึ้น

ตัวอย่างภาษาซี:

int  foo ( ถ่านที่ไม่ได้ลงชื่อ x ) { ค่าint = 2147483600 ; /* สมมติว่า int 32 บิตและถ่าน 8 บิต */ ค่า+= x ; ถ้า( ค่า< 214748360 ) บาร์(); คืนค่า; } 

         
       
        
        
      

ค่าของxไม่สามารถเป็นค่าลบได้ และเนื่องจากโอเวอร์โฟลว์ของจำนวนเต็มที่มีการลงนามนั้นเป็นพฤติกรรมที่ไม่ได้กำหนดไว้ใน C คอมไพเลอร์สามารถสันนิษฐานได้ว่าvalue < 2147483600จะเป็นเท็จเสมอ ดังนั้นifคำสั่ง รวมถึงการเรียกไปยังฟังก์ชันbarคอมไพเลอร์สามารถละเลยได้ เนื่องจากนิพจน์การทดสอบในifนั้นไม่มีผลข้างเคียงและสภาพของคำสั่งนั้นจะไม่มีวันได้รับความพึงพอใจ รหัสจึงมีความหมายเทียบเท่ากับ:

int  foo ( ถ่านที่ไม่ได้ลงชื่อ x ) { ค่าint = 2147483600 ; ค่า+= x ; คืนค่า; } 

        
       
      

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

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

เป็นโมฆะ run_tasks ( ถ่านที่ไม่ได้ลงนาม * ptrx ) { int z ; z = foo ( * ptrx ); ในขณะที่( * ptrx > 60 ) { run_one_task ( ptrx , z ); } }  
     
      
        
         
    

คอมไพเลอร์มีอิสระในการเพิ่มประสิทธิภาพwhile-loop ที่นี่โดยใช้การวิเคราะห์ช่วงค่า : โดยการตรวจสอบfoo()จะรู้ว่าค่าเริ่มต้นที่ชี้ไปptrxนั้นไม่สามารถเกิน 47 ได้ (เนื่องจากจะทำให้เกิดพฤติกรรมที่ไม่ได้กำหนดในfoo()) ดังนั้นการตรวจสอบเริ่มต้นของ*ptrx > 60พินัยกรรม เป็นเท็จเสมอในโปรแกรมที่สอดคล้อง ต่อไป เนื่องจากzตอนนี้ผลลัพธ์ไม่เคยใช้และfoo()ไม่มีผลข้างเคียง คอมไพเลอร์สามารถเพิ่มประสิทธิภาพrun_tasks()ให้เป็นฟังก์ชันว่างที่ส่งคืนได้ทันที การหายตัวไปของwhile-loop อาจจะเป็นที่น่าแปลกใจโดยเฉพาะอย่างยิ่งถ้าfoo()มีการกำหนดไว้ในไฟล์วัตถุรวบรวมแยกต่างหาก

ประโยชน์อีกประการหนึ่งจากการอนุญาตให้โอเวอร์โฟลว์จำนวนเต็มที่ลงนามแล้วไม่สามารถกำหนดได้คือทำให้สามารถจัดเก็บและจัดการค่าของตัวแปรในการลงทะเบียนตัวประมวลผลที่มีขนาดใหญ่กว่าขนาดของตัวแปรในซอร์สโค้ด ตัวอย่างเช่น หากประเภทของตัวแปรตามที่ระบุในซอร์สโค้ดแคบกว่าความกว้างของรีจิสเตอร์ดั้งเดิม (เช่น " int " บนเครื่อง64 บิตซึ่งเป็นสถานการณ์ทั่วไป) คอมไพเลอร์จะสามารถใช้ 64- ที่ลงนามได้อย่างปลอดภัย จำนวนเต็มบิตสำหรับตัวแปรในรหัสเครื่องมันสร้างโดยไม่ต้องเปลี่ยนพฤติกรรมที่กำหนดไว้ของรหัส หากโปรแกรมขึ้นอยู่กับพฤติกรรมของโอเวอร์โฟลว์จำนวนเต็ม 32 บิต คอมไพเลอร์จะต้องแทรกตรรกะเพิ่มเติมเมื่อทำการคอมไพล์สำหรับเครื่อง 64 บิต เนื่องจากพฤติกรรมโอเวอร์โฟลว์ของคำสั่งเครื่องส่วนใหญ่ขึ้นอยู่กับความกว้างของรีจิสเตอร์ [3]

พฤติกรรมที่ไม่ได้กำหนดยังช่วยให้การตรวจสอบมากขึ้นเวลารวบรวมโดยทั้งสองคอมไพเลอร์และการวิเคราะห์โปรแกรมแบบคงที่ [ ต้องการการอ้างอิง ]

ความเสี่ยง

มาตรฐาน C และ C++ มีพฤติกรรมที่ไม่ได้กำหนดหลายรูปแบบตลอด ซึ่งให้เสรีภาพในการใช้งานคอมไพเลอร์ที่เพิ่มขึ้นและการตรวจสอบเวลาคอมไพล์ด้วยค่าใช้จ่ายของพฤติกรรมรันไทม์ที่ไม่ได้กำหนด หากมี โดยเฉพาะอย่างยิ่งมาตรฐาน ISOสำหรับ C มีภาคผนวกที่ระบุแหล่งที่มาทั่วไปของพฤติกรรมที่ไม่ได้กำหนดไว้[4]นอกจากนี้ คอมไพเลอร์ไม่จำเป็นต้องวินิจฉัยโค้ดที่อาศัยพฤติกรรมที่ไม่ได้กำหนดไว้ ดังนั้น จึงเป็นเรื่องปกติสำหรับโปรแกรมเมอร์ แม้แต่คนที่มีประสบการณ์ ยังต้องพึ่งพาพฤติกรรมที่ไม่ได้กำหนดไว้ไม่ว่าจะโดยไม่ได้ตั้งใจ หรือเพียงเพราะพวกเขาไม่รอบรู้ในกฎของภาษาที่สามารถขยายได้หลายร้อยหน้า ซึ่งอาจส่งผลให้เกิดข้อบกพร่องที่เปิดเผยเมื่อมีการใช้คอมไพเลอร์ที่แตกต่างกันหรือการตั้งค่าที่แตกต่างกัน การทดสอบหรือคลุมเครือด้วยการเปิดใช้งานการตรวจสอบพฤติกรรมที่ไม่ได้กำหนดแบบไดนามิก เช่น เครื่องฆ่าเชื้อClangสามารถช่วยในการตรวจจับพฤติกรรมที่ไม่ได้กำหนดซึ่งไม่ได้รับการวินิจฉัยโดยคอมไพเลอร์หรือเครื่องวิเคราะห์แบบสถิต[5]

พฤติกรรมที่ไม่ได้กำหนดอาจนำไปสู่ช่องโหว่ด้านความปลอดภัยในซอฟต์แวร์ ตัวอย่างเช่น บัฟเฟอร์ล้นและช่องโหว่ด้านความปลอดภัยอื่นๆ ในเว็บเบราว์เซอร์หลักเกิดจากการทำงานที่ไม่ได้กำหนดไว้ปี 2038 ปัญหาเป็นอีกตัวอย่างหนึ่งเนื่องจากการลงนาม ล้นจำนวนเต็มเมื่อนักพัฒนาของGCCเปลี่ยนคอมไพเลอร์ในปี 2008 โดยละเว้นการตรวจสอบโอเวอร์โฟลว์ซึ่งอาศัยพฤติกรรมที่ไม่ได้กำหนดไว้CERTได้ออกคำเตือนเกี่ยวกับคอมไพเลอร์เวอร์ชันใหม่กว่า[6] Linux Weekly Newsชี้ให้เห็นว่ามีพฤติกรรมแบบเดียวกันในPathScale C , Microsoft Visual C++ 2005 และคอมไพเลอร์อื่น ๆ อีกหลายตัว [7]คำเตือนได้รับการแก้ไขในภายหลังเพื่อเตือนเกี่ยวกับคอมไพเลอร์ต่างๆ [8]

ตัวอย่างในภาษา C และ C++

รูปแบบหลักของพฤติกรรมที่ไม่ได้กำหนดในภาษา C สามารถจำแนกได้กว้างๆ ดังนี้: [9]การละเมิดความปลอดภัยของหน่วยความจำเชิงพื้นที่, การละเมิดความปลอดภัยของหน่วยความจำชั่วคราว, การล้นของจำนวนเต็ม , การละเมิดนามแฝงที่เข้มงวด, การละเมิดการจัดตำแหน่ง, การแก้ไขที่ไม่ต่อเนื่อง, การแข่งขันของข้อมูล และการวนซ้ำที่ไม่ได้ดำเนินการ I/ O หรือยุติ

ใน C การใช้ใด ๆตัวแปรอัตโนมัติก่อนที่จะได้รับการเริ่มต้นได้อัตราผลตอบแทนที่ไม่ได้กำหนดพฤติกรรมเช่นเดียวกับจำนวนเต็มหารด้วยศูนย์ลงนามจำนวนเต็มล้นจัดทำดัชนีนอกอาร์เรย์ของขอบเขตที่กำหนดไว้ (ดูหน่วยความจำล้น ) หรือnull ชี้ dereferencing โดยทั่วไป อินสแตนซ์ของพฤติกรรมที่ไม่ได้กำหนดจะทำให้เครื่องประมวลผลนามธรรมอยู่ในสถานะที่ไม่รู้จัก และทำให้การทำงานของโปรแกรมทั้งหมดไม่มีการกำหนด

ความพยายามที่จะแก้ไขตัวอักษรสตริงทำให้เกิดพฤติกรรมที่ไม่ได้กำหนด: [10]

ถ่าน * p  =  "วิกิพีเดีย" ;  // ภาษา C ที่ถูกต้อง เลิกใช้แล้วใน C++98/C++03 มีรูปแบบไม่ถูกต้อง ณ C++11 
p [ 0 ]  =  'W' ;  // พฤติกรรมที่ไม่ได้กำหนด

การหารจำนวนเต็มด้วยศูนย์ส่งผลให้เกิดพฤติกรรมที่ไม่ได้กำหนด: [11]

int  x  =  1 ; 
ผลตอบแทน x  /  0 ;  // พฤติกรรมที่ไม่ได้กำหนด

การทำงานของตัวชี้บางอย่างอาจส่งผลให้เกิดพฤติกรรมที่ไม่ได้กำหนด: [12]

int  arr [ 4 ]  =  { 0 ,  1 ,  2 ,  3 }; 
int  * p  =  arr  +  5 ;   // พฤติกรรมที่ไม่ได้กำหนดสำหรับการจัดทำดัชนีนอกขอบเขต
p  =  0 ; 
int  a  =  * p ;         // พฤติกรรมที่ไม่ได้กำหนดสำหรับ dereference ตัวชี้ค่าว่าง

ใน C และ C ++ การเปรียบเทียบความสัมพันธ์ของตัวชี้ไปยังวัตถุ (น้อยกว่าหรือมากกว่ากว่าการเปรียบเทียบ) เป็นเพียงการกำหนดอย่างเคร่งครัดหากตัวชี้ชี้ไปยังสมาชิกของวัตถุเดียวกันหรือองค์ประกอบของเดียวกันอาร์เรย์ [13]ตัวอย่าง:

int  main ( เป็นโมฆะ) 
{ 
  int  a  =  0 ; 
  int  b  =  0 ; 
  กลับมา & a  <  & b ;  /* พฤติกรรมที่ไม่ได้กำหนด */ 
}

การถึงจุดสิ้นสุดของฟังก์ชันการคืนค่า (นอกเหนือจากmain()) โดยไม่มีคำสั่ง return ส่งผลให้เกิดพฤติกรรมที่ไม่ได้กำหนดหากผู้เรียกใช้ค่าของการเรียกใช้ฟังก์ชัน: [14]

int  f () 
{ 
}   /* พฤติกรรมที่ไม่ได้กำหนดหากใช้ค่าของการเรียกใช้ฟังก์ชัน*/

การปรับเปลี่ยนวัตถุระหว่างจุดลำดับสองจุดมากกว่าหนึ่งครั้งทำให้เกิดพฤติกรรมที่ไม่ได้กำหนดไว้ [15]มีการเปลี่ยนแปลงอย่างมากในสิ่งที่ทำให้เกิดพฤติกรรมที่ไม่ได้กำหนดไว้ซึ่งสัมพันธ์กับจุดลำดับ ณ C++11 [16]คอมไพเลอร์สมัยใหม่สามารถส่งคำเตือนเมื่อพบการแก้ไขที่ไม่ต่อเนื่องหลายครั้งในวัตถุเดียวกัน [17] [18]ตัวอย่างต่อไปนี้จะทำให้เกิดการทำงานที่ไม่ได้กำหนดทั้งใน C และ C++

int  f ( int  i )  { 
  return  i ++  +  i ++ ;  /* พฤติกรรมที่ไม่ได้กำหนด: การแก้ไขที่ไม่ต่อเนื่องสองครั้งใน i */ 
}

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

[ ผม]  =  ฉัน++ ;  // พฤติกรรมที่ไม่ได้กำหนด
printf ( "%d %d \n " ,  ++ n ,  power ( 2 ,  n ));  // ยังพฤติกรรมที่ไม่ได้กำหนด

ใน C/C++ ระดับบิตขยับค่าตามจำนวนบิตซึ่งเป็นจำนวนลบหรือมากกว่าหรือเท่ากับจำนวนบิตทั้งหมดในค่านี้ส่งผลให้เกิดพฤติกรรมที่ไม่ได้กำหนดไว้ วิธีที่ปลอดภัยที่สุด (โดยไม่คำนึงถึงผู้จำหน่ายคอมไพเลอร์) คือการรักษาจำนวนบิตที่จะเปลี่ยนเสมอ (ตัวถูกดำเนินการทางขวาของตัวดำเนินการ<<และ>> ตัวดำเนินการระดับบิต ) ภายในช่วง: < > ( ตัวถูกดำเนินการทางซ้ายอยู่ที่ไหน) 0, sizeof(value)*CHAR_BIT - 1value

int  NUM  =  -1 ; 
val int ที่ไม่ได้ลงนาม = 1 << num ; //เลื่อนด้วยจำนวนลบ - พฤติกรรมที่ไม่ได้กำหนด      

num  =  32 ;  //หรือจำนวนใดๆ ที่มากกว่า 31 
val  =  1  <<  num ;  // ตัวอักษร '1' ถูกพิมพ์เป็นจำนวนเต็ม 32 บิต - ในกรณีนี้การขยับมากกว่า 31 บิตเป็นพฤติกรรมที่ไม่ได้กำหนด

num  =  64 ;  //หรือตัวเลขใดๆ ที่มากกว่า 63 
unsigned  long  long  val2  =  1ULL  <<  num ;  // ตัวอักษร '1ULL' ถูกพิมพ์เป็นจำนวนเต็ม 64 บิต - ในกรณีนี้การขยับมากกว่า 63 บิตเป็นพฤติกรรมที่ไม่ได้กำหนด

ดูเพิ่มเติม

อ้างอิง

  1. ^ "จมูกปีศาจ" . ไฟล์ศัพท์แสง . สืบค้นเมื่อ12 มิถุนายน 2557 .
  2. ^ พฤติกรรม GCC Undefined ฆ่าเชื้อ - ubsan
  3. ^ https://gist.github.com/rygorous/e0f055bfb74e3d5f0af20690759de5a7#file-gistfile1-txt-L166
  4. ^ ISO/IEC 9899:2011 §J.2.
  5. ^ จอห์นเรเจหยร "พฤติกรรมที่ไม่ได้กำหนดในปี 2017, 2017 cppcon"
  6. ^ "ช่องโหว่ Note VU # 162289 - GCC เงียบทิ้งบางส่วนตรวจสอบวิจิตร" ช่องโหว่ฐานข้อมูลหมายเหตุ ใบรับรอง 4 เมษายน 2551 เก็บถาวรจากต้นฉบับเมื่อ 9 เมษายน 2551
  7. ^ Jonathan Corbet (16 เมษายน 2551) "GCC และพอยน์เตอร์โอเวอร์โฟลว์" . ลินุกซ์ข่าวประจำสัปดาห์
  8. ^ "ช่องโหว่ Note VU # 162289 - คอมไพเลอร์ C เงียบอาจทิ้งบางส่วนตรวจสอบวิจิตร" ช่องโหว่ฐานข้อมูลหมายเหตุ ใบรับรอง 8 ตุลาคม 2551 [4 เมษายน 2551].
  9. ^ Pascal Cuoq และจอห์นเรเจหยร (4 กรกฎาคม 2017) "พฤติกรรมที่ไม่ได้กำหนดในปี 2560 ฝังตัวในบล็อกวิชาการ" .
  10. ^ ISO / IEC (2003). ISO/IEC 14882:2003(E): ภาษาการเขียนโปรแกรม - C++ §2.13.4 ตัวอักษรสตริง [lex.string]พารา 2
  11. ^ ISO / IEC (2003). ISO/IEC 14882:2003(E): Programming Languages ​​- C++ §5.6 Multiplicative operators [expr.mul] para. 4
  12. ^ ISO / IEC (2003). ISO/IEC 14882:2003(E): Programming Languages ​​- C++ §5.7 Additive operators [expr.add] para. 5
  13. ^ ISO / IEC (2003). ISO/IEC 14882:2003(E): ภาษาการเขียนโปรแกรม - C++ §5.9 ตัวดำเนินการเชิงสัมพันธ์ [expr.rel]วรรค 2
  14. ^ ISO / IEC (2007). ISO/IEC 9899:2007(E): ภาษาการเขียนโปรแกรม - C §6.9 คำจำกัดความภายนอกสำหรับ 1
  15. ^ ANSI X3.159-1989ภาษาการเขียนโปรแกรม C , เชิงอรรถ 26
  16. ^ "ลำดับการประเมิน - cppreference.com" . th.cppreference.com . สืบค้นเมื่อ 2016-08-09.
  17. ^ "ตัวเลือก (การใช้แอฟริกาเก็บสะสม (GCC)) คำเตือน" GCC, แอฟริกาเก็บสะสม - โครงการ GNU - มูลนิธิซอฟต์แวร์เสรี (FSF) สืบค้นเมื่อ2021-07-09 .
  18. ^ "ค่าสถานะการวินิจฉัยใน Clang" . เสียงดังกราว 13 เอกสาร สืบค้นเมื่อ2021-07-09 .
  19. ^ ISO / IEC (1999). ISO/IEC 9899:1999(E): ภาษาการเขียนโปรแกรม - C §6.5 Expressions para 2

อ่านเพิ่มเติม

ลิงค์ภายนอก

  • รุ่นที่ถูกต้องของมาตรฐาน C99 ดูหัวข้อ 6.10.6 สำหรับ #pragma