c++ - क्या "अपरिभाषित व्यवहार" वास्तव में*कुछ भी*होने की अनुमति देता है?




language-lawyer undefined-behavior (6)

अपरिभाषित व्यवहार कंपाइलरों को कुछ मामलों में तेज कोड उत्पन्न करने की अनुमति देता है। दो अलग-अलग प्रोसेसर आर्किटेक्चर पर विचार करें जो अलग से जोड़ें: प्रोसेसर ए स्वाभाविक रूप से कैरी बिट को अतिप्रवाह पर छोड़ देता है, जबकि प्रोसेसर बी एक त्रुटि उत्पन्न करता है। (बेशक, प्रोसेसर सी स्वाभाविक रूप से नाक दानव उत्पन्न करता है - इसका सबसे आसान तरीका यह है कि एक स्नोट-संचालित नैनोबॉट में ऊर्जा के उस अतिरिक्त बिट का निर्वहन ...)

यदि मानक के लिए आवश्यक है कि एक त्रुटि उत्पन्न होती है, तो प्रोसेसर ए के लिए संकलित सभी कोड को मूल रूप से अतिरिक्त निर्देशों को शामिल करने के लिए मजबूर किया जाएगा, ताकि अतिप्रवाह के लिए कुछ प्रकार की जांच हो सके, और यदि ऐसा है, तो एक त्रुटि उत्पन्न करें। यह धीमी कोड में परिणाम होगा, भले ही डेवलपर को पता हो कि वे केवल छोटी संख्याओं को जोड़ने के लिए समाप्त होने जा रहे थे।

अपरिभाषित व्यवहार गति के लिए पोर्टेबिलिटी का त्याग करता है। 'कुछ भी' होने की अनुमति देकर, संकलक उन स्थितियों के लिए सुरक्षा-जांच लिखने से बच सकता है जो कभी नहीं घटित होंगी। (या, आप जानते हैं ... वे हो सकते हैं।)

इसके अतिरिक्त, जब कोई प्रोग्रामर जानता है कि वास्तव में एक अपरिभाषित व्यवहार उनके दिए गए वातावरण में क्या कारण होगा, तो वे अतिरिक्त प्रदर्शन हासिल करने के लिए उस ज्ञान का फायदा उठाने के लिए स्वतंत्र हैं।

यदि आप यह सुनिश्चित करना चाहते हैं कि आपका कोड सभी प्लेटफार्मों पर बिल्कुल एक जैसा व्यवहार करता है, तो आपको यह सुनिश्चित करने की आवश्यकता है कि कभी भी कोई अपरिभाषित व्यवहार न हो - हालांकि, यह आपका लक्ष्य नहीं हो सकता है।

संपादित करें: (OPs के लिए जिम्मेदारी में संपादित करें) कार्यान्वयन परिभाषित व्यवहार नाक राक्षसों की लगातार पीढ़ी की आवश्यकता होगी। अपरिभाषित व्यवहार नाक राक्षसों की छिटपुट पीढ़ी की अनुमति देता है।

यह वह जगह है जहां अपरिभाषित व्यवहार के कार्यान्वयन से अधिक लाभ विशिष्ट व्यवहार प्रकट होता है। विचार करें कि किसी विशेष प्रणाली पर असंगत व्यवहार से बचने के लिए अतिरिक्त कोड की आवश्यकता हो सकती है। इन मामलों में, अपरिभाषित व्यवहार अधिक गति की अनुमति देता है।

इस सवाल का पहले से ही यहाँ एक जवाब है:

संपादित करें: यह सवाल अपरिभाषित व्यवहार के (डी) गुणों के बारे में चर्चा करने के लिए एक मंच के रूप में नहीं था, लेकिन यह ऐसा हो गया। किसी भी मामले में, बिना किसी अपरिभाषित व्यवहार के एक काल्पनिक सी-संकलक के बारे में यह धागा उन लोगों के लिए अतिरिक्त रुचि हो सकती है जो सोचते हैं कि यह एक महत्वपूर्ण विषय है।

"अपरिभाषित व्यवहार" का क्लासिक एपोक्रिफ़ल उदाहरण, निश्चित रूप से, "नाक राक्षसों" - एक भौतिक असंभव है, चाहे सी और सी ++ मानकों की अनुमति हो।

चूँकि C और C ++ समुदाय अपरिभाषित व्यवहार की अप्रत्याशितता पर इतना जोर देते हैं और यह विचार करने के लिए कि संकलक को प्रोग्राम का शाब्दिक कारण कुछ भी करने की अनुमति दी जाती है जब अपरिभाषित व्यवहार का सामना किया जाता है, तो मैंने मान लिया था कि मानक कोई प्रतिबंध नहीं रखता है के व्यवहार पर, ठीक है, अपरिभाषित व्यवहार।

लेकिन C ++ मानक में प्रासंगिक उद्धरण प्रतीत होता है :

[C++14: defns.undefined]: [..] अनुमेय अपरिभाषित व्यवहार पूरी तरह से अप्रत्याशित परिणामों के साथ स्थिति को अनदेखा करने से लेकर, व्यवहार के दौरान या कार्यक्रम के निष्पादन के दौरान पर्यावरण की विशेषता के बिना दस्तावेज या तरीके से निष्पादन के दौरान व्यवहार करने के लिए होता है। एक नैदानिक ​​संदेश), एक अनुवाद या निष्पादन को समाप्त करने के लिए (एक नैदानिक ​​संदेश जारी करने के साथ)। [..]

यह वास्तव में संभावित विकल्पों के एक छोटे सेट को निर्दिष्ट करता है:

  • स्थिति को नजरअंदाज करते हुए - हां, मानक यह कहता है कि इसके "अप्रत्याशित परिणाम" होंगे, लेकिन यह कोड डालने वाले कंपाइलर के समान नहीं है (जो मुझे लगता है कि आपके लिए एक पूर्वापेक्षा होगी, आप जानते हैं, नाक में जकड़न)।
  • प्रलेखित ढंग से व्यवहार करना पर्यावरण की विशेषता है - यह वास्तव में अपेक्षाकृत सौम्य लगता है। (मैं निश्चित रूप से नाक राक्षसों के किसी भी प्रलेखित मामलों के बारे में नहीं सुना है।)
  • अनुवाद या निष्पादन समाप्त करना - निदान के साथ, कम नहीं। क्या सभी यूबी इतनी अच्छी तरह से व्यवहार करेंगे।

मेरा मानना ​​है कि ज्यादातर मामलों में, कंपाइलर अपरिभाषित व्यवहार को अनदेखा करना चुनते हैं; उदाहरण के लिए, जब एकतरफा मेमोरी पढ़ते हैं, तो यह संभवतया एक विरोधी-अनुकूलन होगा जो सुसंगत व्यवहार को सुनिश्चित करने के लिए किसी भी कोड को सम्मिलित करेगा। मुझे लगता है कि अपरिभाषित व्यवहार के अपरिचित प्रकार (जैसे " समय यात्रा ") दूसरी श्रेणी के अंतर्गत आते हैं - लेकिन इसके लिए आवश्यक है कि इस तरह के व्यवहारों को प्रलेखित किया जाए और "पर्यावरण की विशेषता" (इसलिए मुझे लगता है कि नाक राक्षस केवल द्वारा उत्पादित होते हैं हीन कंप्यूटर?)।

क्या मैं परिभाषा को गलत समझ रहा हूं? क्या ये विकल्प के व्यापक सूची के बजाय अपरिभाषित व्यवहार का गठन कर सकते हैं, के मात्र उदाहरण के रूप में? क्या यह दावा है कि "कुछ भी हो सकता है" का अर्थ केवल स्थिति की अनदेखी का एक अप्रत्याशित दुष्प्रभाव है?

EDIT: स्पष्टीकरण के दो मामूली बिंदु:

  • मुझे लगा कि यह मूल प्रश्न से स्पष्ट है, और मुझे लगता है कि ज्यादातर लोग यह थे, लेकिन मैं इसे वैसे भी बाहर कर दूंगा: मुझे एहसास है कि "नाक राक्षस" जीभ-इन-गाल है।
  • कृपया यह बताएं कि यूबी प्लेटफ़ॉर्म-विशिष्ट कंपाइलर ऑप्टिमाइज़ेशन के लिए अनुमति नहीं देता है, जब तक आप यह भी नहीं बताते कि यह अनुकूलन के लिए कैसे अनुमति देता है कि कार्यान्वयन-परिभाषित व्यवहार की अनुमति नहीं देगा।

अनिर्धारित व्यवहार का एक ऐतिहासिक उद्देश्य इस संभावना के लिए अनुमति देना था कि कुछ कार्यों के विभिन्न प्लेटफार्मों पर संभावित रूप से उपयोगी प्रभाव हो सकते हैं। उदाहरण के लिए, सी के शुरुआती दिनों में, दिया गया

int i=INT_MAX;
i++;
printf("%d",i);

कुछ कंपाइलर गारंटी दे सकते हैं कि कोड कुछ विशेष मूल्य (दो-पूरक मशीन के लिए आमतौर पर INT_MIN होगा) प्रिंट करेगा, जबकि अन्य यह गारंटी देंगे कि प्रोग्राम प्रिंटफ तक पहुंचने के बिना समाप्त हो जाएगा। आवेदन आवश्यकताओं के आधार पर, या तो व्यवहार उपयोगी हो सकता है। अपरिभाषित व्यवहार को छोड़ने का मतलब था कि एक आवेदन जहां असामान्य कार्यक्रम समाप्ति अतिप्रवाह का एक स्वीकार्य परिणाम था, लेकिन प्रतीत होता है कि वैध-लेकिन-गलत उत्पादन का उत्पादन नहीं होगा, एक मंच पर चलाने पर अतिप्रवाह की जाँच से गुजरना सकता है जो इसे फंसाता है, और एक आवेदन। जहां अतिप्रवाह के मामले में असामान्य समाप्ति स्वीकार्य नहीं होगी, लेकिन अंकगणित-गलत आउटपुट का उत्पादन होगा, यदि अतिप्रवाह चेकिंग से गुजर सकता है अगर एक मंच पर चलाया जाए जहां ओवरफ्लो फंस नहीं गए थे।

हाल ही में, हालांकि, कुछ संकलक लेखकों ने यह देखने के लिए एक प्रतियोगिता में भाग लिया है कि कौन सबसे कुशलता से किसी भी कोड को समाप्त कर सकता है जिसका अस्तित्व मानक द्वारा अनिवार्य नहीं होगा। उदाहरण के लिए, दिया ...

#include <stdio.h>

int main(void)
{
  int ch = getchar();
  if (ch < 74)
    printf("Hey there!");
  else
    printf("%d",ch*ch*ch*ch*ch);
}

एक हाइपर-मॉडर्न कंपाइलर यह निष्कर्ष निकाल सकता है कि यदि ch 74 या उससे अधिक है, तो ch*ch*ch*ch*ch की गणना अपरिभाषित व्यवहार करेगी, और परिणामस्वरूप प्रोग्राम को "अरे वहाँ!" बिना इस बात की परवाह किए कि किस चरित्र को टाइप किया गया था।


मैंने सोचा था कि मैं आपके एक अंक का उत्तर दूंगा, क्योंकि अन्य उत्तर सामान्य प्रश्न का काफी अच्छी तरह से उत्तर देते हैं, लेकिन इसे अनसुना कर दिया है।

"स्थिति को अनदेखा करना - हां, मानक यह कहता है कि इसके" अप्रत्याशित परिणाम "होंगे, लेकिन यह कोड डालने वाले कंपाइलर के समान नहीं है (जो मुझे लगता है कि आपके लिए एक पूर्वापेक्षा होगी, आप जानते हैं, नासमझ राक्षसों)। "

ऐसी स्थिति जिसमें नाक के राक्षसों को किसी भी कोड को सम्मिलित किए बिना समझदार संकलक के साथ होने की उम्मीद की जा सकती है, निम्नलिखित होंगे:

if(!spawn_of_satan)
    printf("Random debug value: %i\n", *x); // oops, null pointer deference
    nasal_angels();
else
    nasal_demons();

एक संकलक, अगर यह साबित कर सकता है कि * x एक अशक्त सूचक है, तो पूरी तरह से हकदार है, कुछ अनुकूलन के हिस्से के रूप में, "ठीक है" कहने के लिए, इसलिए मैं देखता हूं कि उन्होंने इस की एक शाखा में एक अशक्त सूचक को हटा दिया है। इसलिए, उस शाखा के हिस्से के रूप में मुझे कुछ भी करने की अनुमति है। इसलिए मैं इसलिए इसे अनुकूलित कर सकता हूं: "

if(!spawn_of_satan)
    nasal_demons();
else
    nasal_demons();

"और वहाँ से, मैं इस के लिए अनुकूलन कर सकते हैं:"

nasal_demons();

आप देख सकते हैं कि सही परिस्थितियों में इस प्रकार की चीज़ एक अनुकूलन कंपाइलर के लिए बहुत उपयोगी साबित हो सकती है, और फिर भी आपदा का कारण बन सकती है। मैंने ऐसे मामलों के कुछ समय पहले कुछ उदाहरण देखे थे जहां वास्तव में यह अनुकूलन के लिए महत्वपूर्ण है कि इस प्रकार के मामले का अनुकूलन करने में सक्षम हो। जब मेरे पास अधिक समय हो तो मैं उन्हें खोदने की कोशिश कर सकता हूं।

संपादित करें: एक उदाहरण जो सिर्फ ऐसे मामले की मेरी स्मृति की गहराई से आया है, जहां यह अनुकूलन के लिए उपयोगी है, जहां आप बहुत बार NULL (शायद इनबिल्ड हेल्पर फ़ंक्शंस) होने के लिए एक पॉइंटर की जांच करते हैं, यहां तक ​​कि पहले से ही इसे बंद किए बिना और बिना होने के बाद भी उसे बदल दिया। ऑप्टिमाइज़िंग कंपाइलर देख सकता है कि आपने इसे डिरेल्ड किया है और इसलिए सभी "NULL" चेक को ऑप्टिमाइज़ करते हैं, क्योंकि अगर आपने इसे डिरेल किया है और यह null है, तो कुछ भी होने की अनुमति है, जिसमें सिर्फ "NULL" नहीं है। जाँच करता है। मेरा मानना ​​है कि इसी तरह के तर्क अन्य अपरिभाषित व्यवहार पर लागू होते हैं।


व्यवहार को अपरिभाषित छोड़ने के कारणों में से एक यह है कि संकलनकर्ता को अनुकूलन करते समय जो कुछ भी मान्यताओं को बनाने की अनुमति है।

यदि कोई ऐसी स्थिति मौजूद है जिसे ऑप्टिमाइज़ेशन लागू करना है, तो पकड़ना होगा, और यह शर्त कोड में अपरिभाषित व्यवहार पर निर्भर है, तो कंपाइलर मान सकता है कि यह पूरा हो गया है, क्योंकि एक अनुरूप कार्यक्रम किसी भी अपरिभाषित व्यवहार पर निर्भर नहीं कर सकता है मार्ग। महत्वपूर्ण रूप से, संकलनकर्ता को इन मान्यताओं के अनुरूप होने की आवश्यकता नहीं है। (जो कार्यान्वयन-परिभाषित व्यवहार के लिए मामला नहीं है)

तो मान लीजिए कि आपके कोड में नीचे की तरह एक समान रूप से सम्मिलित उदाहरण है:

int bar = 0;
int foo = (undefined behavior of some kind);
if (foo) {
   f();
   bar = 1;
}
if (!foo) {
   g();
   bar = 1;
}
assert(1 == bar);

संकलक यह मानने के लिए स्वतंत्र है कि फू पहले खंड में सच है और दूसरे में फू सच है, और इस प्रकार कोड के पूरे भाग को अनुकूलित करें। अब, तार्किक रूप से या तो फू या फ़ू को सच होना चाहिए, और इसलिए कोड को देखते हुए, आप यथोचित रूप से यह मान पाएंगे कि आपके द्वारा कोड चलाने के बाद बार को 1 के बराबर होना चाहिए। लेकिन क्योंकि कंपाइलर उस तरीके से ऑप्टिमाइज़ होता है, इसलिए बार कभी सेट नहीं होता है। और अब यह दावा झूठा हो जाता है और प्रोग्राम समाप्त हो जाता है, जो कि ऐसा व्यवहार होता है, जो अगर अपरिभाषित व्यवहार पर निर्भर नहीं होता तो ऐसा नहीं होता।

अब, क्या कंपाइलर के लिए वास्तव में पूरी तरह से नया कोड डालना संभव है अगर यह अपरिभाषित व्यवहार देखता है? यदि ऐसा करने से यह अधिक अनुकूलन करने की अनुमति देगा, बिल्कुल। क्या यह अक्सर होने की संभावना है? शायद नहीं, लेकिन आप इसकी गारंटी कभी नहीं दे सकते हैं, इसलिए इस धारणा पर काम करना कि नाक के राक्षस संभव हैं, एकमात्र सुरक्षित दृष्टिकोण है।


हर C और C ++ मानक में अपरिभाषित व्यवहार की परिभाषा, अनिवार्य रूप से यह है कि मानक क्या होता है पर कोई आवश्यकता नहीं लगाता है।

हां, इसका मतलब है कि किसी भी परिणाम की अनुमति है। लेकिन ऐसा कोई विशेष परिणाम नहीं है, जो होने के लिए आवश्यक हो, और न ही ऐसा कोई परिणाम हो जो आवश्यक नहीं है। इससे कोई फर्क नहीं पड़ता कि आपके पास एक कंपाइलर और लाइब्रेरी है जो लगातार अपरिभाषित व्यवहार के एक विशेष उदाहरण के जवाब में एक विशेष व्यवहार का उत्पादन करता है - इस तरह के व्यवहार की आवश्यकता नहीं है, और आपके कंपाइलर के भविष्य के बगफिक्स रिलीज़ में भी बदल सकता है - और कंपाइलर अभी भी C और C ++ मानकों के प्रत्येक संस्करण के अनुसार पूरी तरह से सही होगा।

यदि आपके मेजबान सिस्टम में आपके नथुने में डाली गई जांच के कनेक्शन के रूप में हार्डवेयर का समर्थन है, तो यह संभावना के दायरे में है कि अपरिभाषित व्यवहार की एक घटना अवांछित नाक प्रभाव का कारण होगी।


हां, यह कुछ भी होने की अनुमति देता है। नोट केवल उदाहरण दे रहा है। परिभाषा बहुत स्पष्ट है:

अपरिभाषित व्यवहार: वह व्यवहार जिसके लिए यह अंतर्राष्ट्रीय मानक कोई आवश्यकता नहीं रखता है।

बार-बार भ्रम की स्थिति:

आपको यह समझना चाहिए कि "कोई आवश्यकता नहीं" का अर्थ यह भी है कि व्यवहार को अपरिभाषित छोड़ने या कुछ विचित्र / नॉनडेटर्मिनिस्टिक करने के लिए कार्यान्वयन की आवश्यकता नहीं है !

कार्यान्वयन को पूरी तरह से सी + + मानक द्वारा कुछ समझदार व्यवहार का दस्तावेजीकरण करने और तदनुसार व्यवहार करने की अनुमति है। 1 इसलिए, यदि आपका संकलक हस्ताक्षरित अतिप्रवाह, लॉजिक (पवित्रता?) पर चारों ओर लपेटने का दावा करता है, तो आप उस कंपाइलर पर उस व्यवहार पर भरोसा करने के लिए स्वागत करेंगे । यदि यह दावा नहीं करता है तो बस दूसरे कंपाइलर से उसी तरह का व्यवहार करने की अपेक्षा न करें।

1 बिल्ली, यह भी एक बात दस्तावेज़ और एक और करने की अनुमति है। यह बेवकूफी होगी, और यह शायद आपको इसे कूड़ेदान में फेंक देगा - आप एक कंपाइलर पर भरोसा क्यों करेंगे, जो आपसे झूठ बोलता है? -लेकिन यह सी ++ मानक के खिलाफ नहीं है।





undefined-behavior