जब d == 0 को शून्य अपवाद द्वारा 'd / = d' डिविजन क्यों नहीं फेंकता है?


81

मुझे यह समझ में नहीं आया कि मुझे शून्य अपवाद द्वारा विभाजन क्यों नहीं मिला:

int d = 0;
d /= d;

मुझे शून्य अपवाद द्वारा एक विभाजन प्राप्त करने की उम्मीद थी लेकिन इसके बजाय d == 1

d /= dशून्य अपवाद द्वारा विभाजन को क्यों नहीं फेंका जाता है d == 0?


25
यह अपरिभाषित व्यवहार है।
LF

51
शून्य अपवाद द्वारा विभाजन जैसी कोई चीज नहीं है।
atν 15:α

15
कुछ टिप्पणियों को स्पष्ट करने के लिए: जब आप एक संदेश "शून्य अपवाद द्वारा विभाजन" के बारे में देखते हैं, तो ऑपरेटिंग सिस्टम आपको बता रहा है कि कुछ गलत हो गया है। यह है नहीं एक C ++ अपवाद। सी ++ में, अपवादों को एक throwबयान द्वारा फेंक दिया जाता है । कुछ और नहीं (जब तक आप अपरिभाषित व्यवहार भूमि में नहीं हैं)।
पीट बेकर

9
सी ++ में " विभाजन द्वारा शून्य अपवाद " जैसी कोई चीज नहीं है ।
अल्जीरदास प्रिदिसियस

6
@ user11659763 "इसीलिए यह एक अपरिभाषित व्यवहार है: यह पूरी तरह से लक्ष्य पर निर्भर करता है।" - यह अपरिभाषित व्यवहार का बिल्कुल मतलब नहीं है ; आप जो वर्णन कर रहे हैं वह कार्यान्वयन-परिभाषित व्यवहार है । अपरिभाषित व्यवहार एक बहुत, बहुत मजबूत कथन है।
12

जवाबों:


108

C ++ में "Zero by Division" अपवाद को पकड़ने के लिए नहीं है। आप जो व्यवहार देख रहे हैं वह कंपाइलर अनुकूलन का परिणाम है:

  1. संकलक मानता है कि अपरिभाषित व्यवहार नहीं होता है
  2. C ++ में Zero द्वारा विभाजन अपरिभाषित व्यवहार है
  3. इसलिए, कोड जो कर सकते हैं शून्य से एक प्रभाग के कारण ऐसा नहीं माना जाता है।
    • और, जो कोड को शून्य द्वारा एक डिवीजन का कारण होना चाहिए वह कभी नहीं होने के लिए माना जाता है
  4. इसलिए, कंपाइलर यह कटौती करता है कि क्योंकि अपरिभाषित व्यवहार नहीं होता है, तो इस कोड में अपरिभाषित व्यवहार के लिए शर्तें ( d == 0) नहीं होनी चाहिए
  5. इसलिए, d / dहमेशा 1 के बराबर होना चाहिए।

तथापि...

हम संकलक को आपके कोड में एक मामूली ट्विक के साथ शून्य द्वारा "वास्तविक" विभाजन को ट्रिगर करने के लिए मजबूर कर सकते हैं।

volatile int d = 0;
d /= d; //What happens?

तो अब सवाल यह है कि अब हमने मूल रूप से संकलक को ऐसा करने की अनुमति देने के लिए मजबूर किया है, क्या होता है? यह अपरिभाषित व्यवहार है- लेकिन अब हमने कंपाइलर को इस अपरिभाषित व्यवहार के आसपास अनुकूलन करने से रोक दिया है।

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

हालाँकि, प्रोग्राम को क्रैश होने के बजाय हार्डवेयर अपवाद (यदि ऐसा होता है) से निपटने के तरीके हैं: इस पोस्ट को कुछ तरीकों के लिए देखें जो लागू हो सकते हैं: अपवाद को पकड़ना: शून्य से विभाजित करना । ध्यान दें कि वे संकलक से संकलक तक भिन्न होते हैं।


25
@ एड्रियन उन दोनों बिल्कुल ठीक हैं क्योंकि व्यवहार अपरिभाषित है। शाब्दिक रूप से कुछ भी ठीक है।
जेसपर जुहल

9
"C ++ में Zero द्वारा विभाजन अपरिभाषित व्यवहार है" -> ध्यान दें कि कंपाइलर IEE754 के तहत फ्लोटिंग पॉइंट प्रकारों के लिए यह अनुकूलन नहीं कर सकता है। इसे NaN पर सेट करना होगा।
बाथशीबा

6
@RichardHodges IEEE754 के तहत संकलित होने वाले एक डबल के लिए अनुकूलन करने की अनुमति नहीं है: NaN का उत्पादन किया जाना चाहिए।
बथशेबा

2
@formerlyognas: यह "यूबी को दूर करने" का मामला नहीं है - यह अभी भी मामला है कि "कुछ भी हो सकता है"; यह सिर्फ इतना है कि उत्पादन 1पूरी तरह से मान्य कुछ भी है। 14684554 हो रही होनी चाहिए क्योंकि संकलक आगे भी अनुकूलन करता है - यह प्रारंभिक d==0स्थिति का प्रचार करता है और इसलिए न केवल "यह या तो 1 या यूबी" है, लेकिन वास्तव में "यह यूबी, अवधि" है। इसलिए यह भी कोड का उत्पादन करने के लिए परेशान नहीं करता है जो निरंतर लोड करता है 1
हमखोलम ने मोनिका

1
लोग हमेशा अनुकूलन को रोकने के लिए अस्थिरता का सुझाव दे रहे हैं, लेकिन एक वाष्पशील पढ़ने या लिखने से क्या होता है कार्यान्वयन-परिभाषित।
दर्शनशास्त्र

38

अन्य उत्तरों के पूरक के लिए, यह तथ्य कि शून्य से विभाजन अपरिभाषित व्यवहार है, इसका मतलब है कि संकलक उन मामलों में कुछ भी करने के लिए स्वतंत्र है जहां यह होगा:

  • संकलक मान सकता है कि 0 / 0 == 1और तदनुसार अनुकूलित करें। यह प्रभावी रूप से यहां दिखाई देता है।
  • संकलक भी, अगर यह चाहता था, मान सकता है 0 / 0 == 42और dउस मूल्य पर सेट कर सकता है।
  • संकलक यह भी तय कर सकता है कि मान का dअनिश्चित है, और इस तरह चर को असंवैधानिक रूप से छोड़ दिया जाता है, ताकि इसका मूल्य वही हो जाए जो पहले से इसके लिए आवंटित मेमोरी में लिखा जाए। टिप्पणियों में अन्य संकलक पर देखे गए कुछ अप्रत्याशित मूल्य उन संकलक द्वारा ऐसा कुछ करने के कारण हो सकते हैं।
  • कंपाइलर प्रोग्राम को निरस्त करने या शून्य से कोई भी विभाजन होने पर एक अपवाद बढ़ाने का निर्णय ले सकता है। चूंकि, इस कार्यक्रम के लिए, कंपाइलर यह निर्धारित कर सकता है कि यह हमेशा होगा, यह केवल अपवाद को बढ़ाने के लिए कोड का उत्सर्जन कर सकता है (या पूरी तरह से निष्पादन को निरस्त कर सकता है) और शेष फ़ंक्शन को अगम्य कोड के रूप में मानता है।
  • जब शून्य से विभाजन होता है तो एक अपवाद को बढ़ाने के बजाय, कंपाइलर प्रोग्राम को रोकने और इसके बजाय सॉलिटेयर का खेल शुरू करने का विकल्प चुन सकता है। वह भी "अपरिभाषित व्यवहार" की छतरी के नीचे आता है।
  • सिद्धांत रूप में, कंपाइलर उस कोड को भी जारी कर सकता है जिसके कारण कंप्यूटर तब भी फट सकता है जब भी कोई विभाजन शून्य से होता है। C ++ मानक में ऐसा कुछ भी नहीं है जो इसे मना करे। (कुछ प्रकार के अनुप्रयोगों के लिए, मिसाइल फ़्लाइट कंट्रोलर की तरह, इसे एक वांछनीय सुरक्षा विशेषता भी माना जा सकता है!)
  • इसके अलावा, मानक स्पष्ट रूप से "समय यात्रा" के लिए अपरिभाषित व्यवहार की अनुमति देता है , ताकि कंपाइलर शून्य होने से पहले विभाजन (या कुछ और) से ऊपर की चीजों को भी कर सके । मूल रूप से, मानक कंपाइलर को स्वतंत्र रूप से संचालन के संचालन की अनुमति देता है, जब तक कि प्रोग्राम के अवलोकन योग्य व्यवहार को बदल नहीं दिया जाता है - लेकिन यह भी कि अंतिम आवश्यकता को स्पष्ट रूप से माफ कर दिया जाता है यदि प्रोग्राम को निष्पादित करने के लिए अपरिभाषित व्यवहार होता है। इसलिए, प्रभाव में, किसी भी कार्यक्रम के निष्पादन का संपूर्ण व्यवहार, जो किसी बिंदु पर, अपरिभाषित व्यवहार को ट्रिगर करना अपरिभाषित है!
  • उपरोक्त के परिणामस्वरूप, कंपाइलर बस यह भी मान सकता है कि अपरिभाषित व्यवहार नहीं होता है , क्योंकि एक प्रोग्राम के लिए एक अनुमेय व्यवहार जो कुछ इनपुट पर अपरिभाषित तरीके से व्यवहार करेगा, वह यह है कि इसके लिए केवल व्यवहार करना है जैसे कि इनपुट कुछ था और । यही है, भले ही dसंकलन समय पर मूल मूल्य ज्ञात नहीं था, फिर भी संकलक यह मान सकता है कि यह कभी शून्य नहीं है और तदनुसार कोड का अनुकूलन करें। ओपी के कोड के विशेष मामले में, इस संकलक सिर्फ इतना है कि यह सोचते से प्रभावी ढंग से अप्रभेद्य है 0 / 0 == 1, लेकिन संकलक भी, उदाहरण के लिए, मान लें कि हो सकता है कि puts()में if (d == 0) puts("About to divide by zero!"); d /= d;कभी नहीं निष्पादित हो जाता है!

29

शून्य से पूर्णांक विभाजन का व्यवहार C ++ मानक द्वारा अपरिभाषित है। यह है नहीं एक अपवाद फेंक करने के लिए आवश्यक।

(शून्य से फ़्लोटिंग पॉइंट डिवीज़न भी अपरिभाषित है लेकिन IEEE754 इसे परिभाषित करता है।)

आपका कंपाइलर d /= dप्रभावी ढंग से अनुकूलन कर रहा है, d = 1जो बनाने के लिए एक उचित विकल्प है। यह इस अनुकूलन को बनाने की अनुमति है क्योंकि यह मानने की अनुमति है कि आपके कोड में कोई अपरिभाषित व्यवहार dनहीं है - जो संभवतः शून्य नहीं हो सकता है।


3
यह स्पष्ट होना महत्वपूर्ण है, कि कुछ और भी हो सकता है, IOW इस व्यवहार पर भरोसा नहीं किया जा सकता है।
हाइड

2
जब आप कहते हैं कि यह संकलक के लिए उचित है "मान लें कि dसंभवतः शून्य नहीं हो सकता है," क्या आप यह भी मानते हैं कि संकलक लाइन नहीं देखता है: int d = 0;?? :)
एड्रियन मोल

6
कंपाइलर इसे देखता है, लेकिन शायद परवाह नहीं करता है। इस तरह के एक किनारे मामले के लिए पहले से ही पागल जटिल संकलक में आवश्यक अतिरिक्त कोड जटिलता शायद इसके लायक नहीं है।
15:45 बजे user4581301

1
@ user4581301 दोनों को एक साथ लेने से यह एक जहरीली शाखा का पता लगाने की अनुमति देता है, जिससे यह बहुत अधिक कोड को prune करता है। तो यह उपयोगी होगा।
Deduplicator

3
इसलिए यदि आपने "int d = 0; if (d == 0) प्रिंटफ (" d = शून्य \ n ") लिखा है, तो d / = d; संकलक प्रिंटफ को भी हटा सकता है।
gnasher729

0

ध्यान दें कि आप अपने कोड को (और अन्य मामलों में) एक सी + अपवाद उत्पन्न कर सकते हैं, जो सुरक्षित अंकशास्त्र का उपयोग करके किया जाता है। https://github.com/boostorg/safe_numerics

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.