พฤติกรรมที่ไม่ได้กำหนดไม่ระบุและไม่ได้กำหนดและการนำไปปฏิบัติ


530

พฤติกรรมที่ไม่ได้กำหนดใน C และ C ++ คืออะไร สิ่งที่เกี่ยวกับพฤติกรรมที่ไม่ระบุและพฤติกรรมที่กำหนดไว้ในการนำไปใช้ ความแตกต่างระหว่างพวกเขาคืออะไร?


1
ฉันค่อนข้างแน่ใจว่าเราเคยทำมาก่อน แต่ฉันหามันไม่เจอ ดูเพิ่มเติม: stackoverflow.com/questions/2301372/…
dmckee --- อดีตผู้ดูแล kitten



1
นี่คือการสนทนาที่น่าสนใจ (ส่วน "ภาคผนวก L และพฤติกรรมที่ไม่ได้กำหนด")
โอเวน

คำตอบ:


405

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

ลองดูตัวอย่างคลาสสิก:

#include <iostream>

int main()
{
    char* p = "hello!\n";   // yes I know, deprecated conversion
    p[0] = 'y';
    p[5] = 'w';
    std::cout << p;
}

ตัวแปรpชี้ไปที่ตัวอักษรของสตริง"hello!\n"และการมอบหมายสองรายการด้านล่างพยายามแก้ไขสตริงตัวอักษรนั้น โปรแกรมนี้ทำอะไร ตามมาตรา 2.14.5 วรรค 11 ของมาตรฐาน C ++ จะเรียกใช้การทำงานที่ไม่ได้กำหนด :

ผลของการพยายามแก้ไขสตริงตัวอักษรจะไม่ได้กำหนด

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

ตัวอย่างอื่น ๆ ของพฤติกรรมที่ไม่ได้กำหนดรวมถึงการเข้าถึงอาร์เรย์เกินขอบเขตของมัน, dereferencing ชี้โมฆะ , การเข้าถึงวัตถุหลังจากที่ชีวิตของพวกเขาจบลงหรือเขียนสำนวนที่ถูกกล่าวหาว่าฉลาดi++ + ++iเช่น

มาตรา 1.9 ของ C ++ มาตรฐานยังกล่าวถึงไม่ได้กำหนดพฤติกรรมของพี่ชายสองคนที่อันตรายน้อยกว่าพฤติกรรมที่ไม่ระบุและพฤติกรรมการดำเนินงานที่กำหนดไว้ :

คำอธิบายความหมายในมาตรฐานสากลนี้กำหนดเครื่องนามธรรมที่เป็นพารามิเตอร์

บางแง่มุมและการทำงานของเครื่องนามธรรมมีการอธิบายไว้ในมาตรฐานสากลนี้ตามที่กำหนดไว้ในการนำไปใช้ (ตัวอย่างเช่นsizeof(int)) สิ่งเหล่านี้เป็นพารามิเตอร์ของเครื่องนามธรรม การใช้งานแต่ละครั้งจะต้องมีเอกสารอธิบายลักษณะและพฤติกรรมของตนในส่วนที่เกี่ยวข้อง

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

การดำเนินการอื่น ๆ บางอย่างมีการอธิบายไว้ในมาตรฐานสากลนี้ว่าไม่ได้กำหนด (ตัวอย่างเช่นผลกระทบของการยกเลิกการอ้างอิงตัวชี้โมฆะ) [ หมายเหตุ : มาตรฐานสากลนี้ไม่มีข้อกำหนดเกี่ยวกับพฤติกรรมของโปรแกรมที่มีพฤติกรรมที่ไม่ได้กำหนด - บันทึกท้าย ]

โดยเฉพาะอย่างยิ่งส่วน 1.3.24 ฯ :

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

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


6
มันเป็นความจริงที่แปลกซึ่งเป็นผลมาจากการรวมที่คำตอบนี้ครอบคลุมเฉพาะ C ++ เท่านั้น แต่แท็กของคำถามนี้รวมถึง C C มีแนวคิดที่แตกต่างกันของ "พฤติกรรมที่ไม่ได้กำหนด": มันจะยังคงต้องการการนำ ไม่ได้กำหนดไว้สำหรับการละเมิดกฎบางอย่าง (การละเมิดข้อ จำกัด )
Johannes Schaub - litb

8
@Benoit มันเป็นพฤติกรรมที่ไม่ได้กำหนดเพราะมาตรฐานบอกว่ามันเป็นพฤติกรรมที่ไม่ได้กำหนดระยะเวลา ในบางระบบจริงตัวอักษรสตริงจะถูกเก็บไว้ในส่วนข้อความแบบอ่านอย่างเดียวและโปรแกรมจะล้มเหลวหากคุณพยายามที่จะแก้ไขตัวอักษรสตริง ในระบบอื่น ๆ ตัวอักษรสตริงจะปรากฏการเปลี่ยนแปลงแน่นอน มาตรฐานไม่ได้บังคับสิ่งที่จะเกิดขึ้น นั่นคือสิ่งที่พฤติกรรมไม่ได้กำหนดหมายถึง
fredoverflow

5
@FredOverflow ทำไมคอมไพเลอร์ที่ดีทำให้เราสามารถคอมไพล์โค้ดที่แสดงพฤติกรรมที่ไม่ได้กำหนดได้อย่างไร สิ่งที่ดีในการรวบรวมโค้ดประเภทนี้คืออะไร? ทำไมคอมไพเลอร์ที่ดีไม่ให้สัญญาณเตือนสีแดงขนาดใหญ่แก่เราเมื่อเราพยายามคอมไพล์โค้ดที่แสดงพฤติกรรมที่ไม่ได้กำหนด?
Pacerier

14
@Pierier มีบางสิ่งที่ไม่สามารถตรวจสอบได้ในเวลารวบรวม ตัวอย่างเช่นเป็นไปไม่ได้เสมอที่จะรับประกันได้ว่าตัวชี้ null จะไม่ถูกยกเลิกการลงทะเบียน แต่สิ่งนี้ไม่ได้กำหนดไว้
Tim Seguine

4
@Celeritas พฤติกรรมที่ไม่ได้กำหนดอาจไม่สามารถกำหนดได้ ตัวอย่างเช่นเป็นไปไม่ได้ที่จะรู้ล่วงหน้าว่าเนื้อหาของหน่วยความจำที่ไม่ได้เตรียมไว้นั้นจะเป็นเช่นไร int f(){int a; return a;}: ค่าaอาจเปลี่ยนแปลงระหว่างการเรียกใช้ฟังก์ชัน
ทำเครื่องหมาย

97

ทีนี้นี่คือการคัดลอกวางตรงจากมาตรฐาน

3.4.1 1 พฤติกรรมที่กำหนดโดยการนำไปใช้งานไม่ได้ระบุพฤติกรรมที่แต่ละเอกสารการนำไปปฏิบัติจะมีวิธีการเลือกอย่างไร

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

3.4.3 1 พฤติกรรมพฤติกรรมที่ไม่ได้กำหนดเมื่อใช้โครงสร้างโปรแกรมที่ไม่สามารถทำได้หรือผิดพลาดหรือข้อมูลผิดพลาดซึ่งมาตรฐานสากลนี้ไม่มีข้อกำหนด

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

3 ตัวอย่างตัวอย่างของพฤติกรรมที่ไม่ได้กำหนดคือพฤติกรรมของจำนวนเต็มล้น

3.4.4การใช้พฤติกรรมที่ไม่ระบุ 1 ค่าที่ไม่ระบุหรือพฤติกรรมอื่น ๆ ที่มาตรฐานสากลนี้ให้ความเป็นไปได้สองทางหรือมากกว่าและไม่มีข้อกำหนดเพิ่มเติมใด ๆ ซึ่งถูกเลือกในกรณี

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


3
ความแตกต่างระหว่างพฤติกรรมที่กำหนดโดยการนำไปใช้และพฤติกรรมที่ไม่ระบุคืออะไร
Zolomon

26
@Zolomon: เหมือนกับที่กล่าวไว้: พื้นฐานในสิ่งเดียวกันยกเว้นว่าในกรณีของการใช้งานที่กำหนดการดำเนินการจะต้องมีเอกสาร (เพื่อรับประกัน) สิ่งที่จะเกิดขึ้นในขณะที่ในกรณีที่ไม่ได้ระบุการใช้งานไม่จำเป็นต้องเอกสาร หรือรับประกันอะไร
AnT

1
@Zolomon: มันสะท้อนให้เห็นในความแตกต่างระหว่าง 3.4.1 และ 2.4.4
sbi

8
@Celeritas: คอมไพเลอร์ Hyper-modern สามารถทำได้ดีกว่านั้น ให้int foo(int x) { if (x >= 0) launch_missiles(); return x << 1; }คอมไพเลอร์สามารถกำหนดได้ว่าเนื่องจากการเรียกใช้ฟังก์ชันที่ไม่เปิดใช้ขีปนาวุธจะเรียกใช้พฤติกรรมที่ไม่ได้กำหนดซึ่งสามารถเรียกใช้งานได้launch_missiles()โดยไม่มีเงื่อนไข
supercat

2
@northerner ในฐานะรัฐอ้างอิงพฤติกรรมที่ไม่ได้ระบุมักจะถูก จำกัด ให้มีพฤติกรรมที่เป็นไปได้จำนวน จำกัด ในบางกรณีคุณอาจสรุปได้ว่าความเป็นไปได้ทั้งหมดนี้เป็นที่ยอมรับในบริบทที่กำหนดซึ่งในกรณีที่พฤติกรรมที่ไม่ได้ระบุไม่ได้เป็นปัญหาเลย พฤติกรรมที่ไม่ได้กำหนดไม่ จำกัด อย่างสมบูรณ์ (eb "โปรแกรมอาจตัดสินใจฟอร์แมตฮาร์ดไดรฟ์ของคุณ") พฤติกรรมที่ไม่ได้กำหนดเป็นปัญหาเสมอ
AnT

60

การใช้ถ้อยคำง่าย ๆ อาจเข้าใจได้ง่ายกว่านิยามที่เข้มงวดของมาตรฐาน

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

พฤติกรรมที่ไม่ได้กำหนด
คุณกำลังทำบางสิ่งผิดปกติ ตัวอย่างเช่นคุณมีค่ามากในที่ไม่พอดีint charคุณจะใส่ค่าที่ได้charอย่างไร จริงๆแล้วไม่มีทาง! อะไรจะเกิดขึ้น แต่สิ่งที่เหมาะสมที่สุดจะนำไบต์แรกของ int charที่และใส่ไว้ใน มันผิดที่จะทำเช่นนั้นเพื่อกำหนดไบต์แรก แต่นั่นคือสิ่งที่เกิดขึ้นภายใต้ประทุน

ไม่ได้ระบุพฤติกรรม
ฟังก์ชั่นของทั้งสองนี้จะถูกดำเนินการครั้งแรก?

void fun(int n, int m);

int fun1()
{
  cout << "fun1";
  return 1;
}
int fun2()
{
  cout << "fun2";
  return 2;
}
...
fun(fun1(), fun2()); // which one is executed first?

ภาษาไม่ได้ระบุการประเมินผลจากซ้ายไปขวาหรือขวาไปซ้าย! ดังนั้นพฤติกรรมที่ไม่ได้ระบุอาจหรืออาจไม่ส่งผลให้เกิดพฤติกรรมที่ไม่ได้กำหนด แต่แน่นอนว่าโปรแกรมของคุณไม่ควรสร้างพฤติกรรมที่ไม่ระบุ


@eSKay ฉันคิดว่าคำถามของคุณควรแก้ไขคำตอบเพื่อชี้แจงเพิ่มเติม :)

สำหรับfun(fun1(), fun2());พฤติกรรม "การใช้งานที่กำหนดไว้" ไม่ใช่หรือ คอมไพเลอร์ต้องเลือกอย่างใดอย่างหนึ่งหรือหลักสูตรอื่นหลังจากทั้งหมด?

ความแตกต่างระหว่างการใช้งานที่กำหนดและไม่ได้ระบุคือคอมไพเลอร์ควรเลือกพฤติกรรมในกรณีแรก แต่ไม่จำเป็นต้องใช้ในกรณีที่สอง sizeof(int)ยกตัวอย่างเช่นการดำเนินการจะต้องมีเพียงหนึ่งเดียวและความหมายของ ดังนั้นจึงไม่สามารถบอกได้ว่าsizeof(int)เป็น 4 สำหรับโปรแกรมบางส่วนและ 8 สำหรับโปรแกรมอื่น ซึ่งแตกต่างจากพฤติกรรมที่ไม่ระบุที่คอมไพเลอร์สามารถพูดว่าตกลงฉันจะประเมินข้อโต้แย้งเหล่านี้จากซ้ายไปขวาและข้อโต้แย้งของฟังก์ชั่นต่อไปจะได้รับการประเมินจากขวาไปซ้าย มันสามารถเกิดขึ้นในโปรแกรมเดียวกันที่ว่าทำไมมันจะเรียกว่ายังไม่ระบุ ในความเป็นจริง C ++ สามารถทำได้ง่ายขึ้นหากมีการระบุพฤติกรรมที่ไม่ระบุบางอย่าง ลองดูที่คำตอบของ Dr. Stroustrupที่นี่:

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

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


1
สำหรับfun(fun1(), fun2());พฤติกรรมที่"implementation defined"ไม่? คอมไพเลอร์ต้องเลือกอย่างใดอย่างหนึ่งหรือหลักสูตรอื่น ๆ หลังจากทั้งหมด?
Lazer

1
@AraK: ขอบคุณสำหรับการอธิบาย ฉันเข้าใจแล้ว Btw "I am gonna evaluate these arguments left-to-right and the next function's arguments are evaluated right-to-left"ฉันเข้าใจสิ่งนี้canเกิดขึ้น จริง ๆ แล้วมันมีคอมไพเลอร์ที่เราใช้วันนี้หรือไม่?
Lazer

1
@eSKay คุณต้องถามกูรูเกี่ยวกับเรื่องนี้ที่ทำให้มือของเขาสกปรกด้วยคอมไพเลอร์มากมาย :) AFAIK VC จะประเมินข้อโต้แย้งจากขวาไปซ้ายเสมอ
AraK

4
@ เลเซอร์: มันสามารถเกิดขึ้นได้อย่างแน่นอน สถานการณ์ง่าย ๆ : foo (bar, boz ()) และ foo (boz (), bar) โดยที่ bar เป็น int และ boz () เป็นฟังก์ชันที่คืนค่า int สมมติซีพียูที่คาดว่าพารามิเตอร์จะถูกส่งผ่านในการลงทะเบียน R0-R1 ผลลัพธ์ของฟังก์ชันถูกส่งคืนใน R0; ฟังก์ชั่นอาจใช้ถังขยะ R1 การประเมิน "บาร์" ก่อนหน้า "boz ()" จะต้องบันทึกสำเนาของบาร์ที่อื่นก่อนที่จะโทร boz () จากนั้นโหลดสำเนาที่บันทึกไว้ การประเมิน "บาร์" หลัง "boz ()" จะหลีกเลี่ยงการเก็บหน่วยความจำและดึงข้อมูลซ้ำและเป็นการเพิ่มประสิทธิภาพที่คอมไพเลอร์จำนวนมากจะทำโดยไม่คำนึงถึงลำดับในรายการอาร์กิวเมนต์
supercat

6
ฉันไม่รู้เกี่ยวกับ C ++ แต่มาตรฐาน C บอกว่าการแปลง int ไปเป็น char นั้นมีการใช้งานที่กำหนดไว้หรือกำหนดไว้อย่างดี (ขึ้นอยู่กับค่าจริงและการเซ็นชื่อของประเภท) ดู C99 §6.3.1.3 (ไม่เปลี่ยนแปลงใน C11)
นิโคไล Ruhe

27

จากเอกสารเหตุผล C อย่างเป็นทางการ

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

พฤติกรรมที่ไม่ได้ระบุจะทำให้ผู้ติดตั้งใช้งานละติจูดในการแปลโปรแกรม ละติจูดนี้ไม่ขยายจนล้มเหลวในการแปลโปรแกรม

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

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


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

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

10

พฤติกรรมที่ไม่ได้กำหนดกับพฤติกรรมที่ไม่ระบุมีคำอธิบายสั้น ๆ

สรุปสุดท้ายของพวกเขา:

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


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

8

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

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

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

ตัวอย่างเช่นให้รหัสต่อไปนี้:

int scaled_velocity(int v, unsigned char pow)
{
  if (v > 250)
    v = 250;
  if (v < -250)
    v = -250;
  return v << pow;
}

การใช้งานที่สมบูรณ์แบบของสองคนนั้นไม่จำเป็นต้องใช้ความพยายามใด ๆ ในการรักษานิพจน์v << powเป็นการเปลี่ยนแปลงแบบสององค์ประกอบโดยไม่คำนึงว่าจะvเป็นบวกหรือลบ

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


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

แต่ความจริงที่ว่า<<UB นั้นคือจำนวนลบเป็นกับดักเล็ก ๆ ที่น่ารังเกียจและฉันดีใจที่ได้รับการเตือนว่า!
Tom Swirly

1
@ TomSwirly: แต่น่าเสียดายที่ผู้เขียนคอมไพเลอร์ไม่สนใจว่าการรับประกันพฤติกรรมที่ผิดเพี้ยนไปเกินกว่าที่มาตรฐานกำหนดไว้มักจะช่วยให้เพิ่มความเร็วได้อย่างมากเมื่อเทียบกับการกำหนดให้รหัสนั้นหลีกเลี่ยงค่าใช้จ่ายใด ๆ ถ้าเป็นโปรแกรมเมอร์ไม่ได้สนใจว่าi+j>kอัตราผลตอบแทน 1 หรือ 0 ในกรณีที่นอกจากล้นที่มีให้มันมีผลข้างเคียงอื่น ๆ , (int)((unsigned)i+j) > kคอมไพเลอร์อาจจะไม่สามารถที่จะทำให้การเพิ่มประสิทธิภาพขนาดใหญ่บางอย่างที่จะไม่เป็นไปได้ถ้าโปรแกรมเมอร์เขียนโค้ดเป็น
supercat

1
@TomSwirly: สำหรับพวกเขาหากคอมไพเลอร์ X สามารถใช้โปรแกรมที่สอดคล้องอย่างเคร่งครัดเพื่อทำงานบางอย่าง T และให้ผลการปฏิบัติการที่มีประสิทธิภาพมากกว่าคอมไพเลอร์ 5% Y จะให้ผลกับโปรแกรมเดียวกันนั่นหมายความว่า X ดีกว่าแม้ว่า Y สามารถสร้างรหัสที่ทำงานเดียวกันสามครั้งอย่างมีประสิทธิภาพเนื่องจากโปรแกรมที่ใช้ประโยชน์จากพฤติกรรมที่ Y รับประกัน แต่ X ไม่ได้
supercat

6

C ++ มาตรฐาน n3337 § 1.3.10 พฤติกรรมที่กำหนดโดยการนำไปปฏิบัติ

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

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


C ++ มาตรฐาน n3337 § 1.3.24 พฤติกรรมที่ไม่ได้กำหนด

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

เมื่อโปรแกรมพบโครงสร้างที่ไม่ได้กำหนดตามมาตรฐาน C ++ อนุญาตให้ทำสิ่งที่ต้องการ (อาจส่งอีเมลถึงฉันหรืออาจส่งอีเมลถึงคุณหรืออาจเพิกเฉยต่อรหัสทั้งหมด)


c ++ มาตรฐาน n3337 § 1.3.25 พฤติกรรมที่ไม่ระบุ

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

C ++ Standard ไม่ได้กำหนดพฤติกรรมที่เฉพาะเจาะจงในโครงสร้างบางอย่าง แต่บอกว่าต้องเลือกพฤติกรรมที่กำหนดไว้อย่างดีโดยเฉพาะ ( ไม่จำเป็นต้องอธิบายบอท ) โดยการใช้งานเฉพาะ ดังนั้นในกรณีที่ไม่มีคำอธิบายใด ๆ ผู้ใช้อาจทราบได้ยากว่าโปรแกรมจะทำงานอย่างไร


6

การดำเนินการที่กำหนดไว้ -

ผู้ปฏิบัติงานต้องการให้มีการบันทึกไว้อย่างดีมาตรฐานให้ทางเลือก แต่ต้องแน่ใจว่าได้รวบรวม

ไม่ได้ระบุ -

เหมือนกับการใช้งานที่กำหนด แต่ไม่ได้ทำเป็นเอกสาร

ไม่ได้กำหนด-

อาจมีอะไรเกิดขึ้นดูแลมัน


2
ฉันคิดว่ามันเป็นสิ่งสำคัญที่จะต้องทราบว่าความหมายในทางปฏิบัติของ "ไม่ได้กำหนด" มีการเปลี่ยนแปลงในช่วงไม่กี่ปีที่ผ่านมา มันเคยเป็นที่ได้รับuint32_t s;การประเมิน1u<<sเมื่อs33 อาจคาดหวังว่าจะได้รับผลตอบแทน 0 หรืออาจจะเป็นผลตอบแทน 2 แต่ไม่ได้ทำอะไรแปลกประหลาดอื่น คอมไพเลอร์ที่ใหม่กว่าอย่างไรก็ตามการประเมิน1u<<sอาจทำให้คอมไพเลอร์ตรวจสอบว่าเนื่องจากsต้องมีน้อยกว่า 32 ก่อนรหัสใด ๆ ก่อนหรือหลังการแสดงออกซึ่งจะเกี่ยวข้องเฉพาะถ้าsมี 32 หรือมากกว่าอาจถูกละเว้น
supercat
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.