आइए देखें कि सी मानक "व्यवहार" और "अपरिभाषित व्यवहार" शब्दों को कैसे परिभाषित करता है।
संदर्भ आईएसओ सी 2011 मानक के एन 1570 ड्राफ्ट के हैं; मैं प्रकाशित तीन सी आईएसओ मानकों (1990, 1999 और 2011) में से किसी में भी प्रासंगिक अंतरों से अवगत नहीं हूं।
धारा 3.4:
व्यवहार
बाहरी रूप या क्रिया
ठीक है, यह थोड़ा अस्पष्ट है, लेकिन मैं तर्क दूंगा कि किसी दिए गए कथन में कोई "उपस्थिति" नहीं है, और निश्चित रूप से कोई "कार्रवाई" नहीं है, जब तक कि इसे वास्तव में निष्पादित नहीं किया जाता है।
धारा 3.4.3:
अपरिभाषित
या गलत प्रोग्राम निर्माण, या गलत डेटा के उपयोग पर अपरिभाषित व्यवहार व्यवहार, जिसके लिए यह अंतर्राष्ट्रीय मानक कोई आवश्यकता नहीं लगाता है
इसे कहते हैं " इस तरह के एक निर्माण का " उपयोग पर । शब्द "उपयोग" मानक द्वारा परिभाषित नहीं है, इसलिए हम सामान्य अंग्रेजी अर्थ में वापस आते हैं। यदि इसे कभी निष्पादित नहीं किया जाता है तो एक निर्माण "उपयोग" नहीं किया जाता है।
उस परिभाषा के तहत एक नोट है:
नोट संभव अपरिभाषित व्यवहार, अप्रत्याशित परिणामों के साथ स्थिति को पूरी तरह से नजरअंदाज करने से लेकर, अनुवाद या प्रोग्राम निष्पादन के दौरान व्यवहार करने के लिए पर्यावरण की एक विशेषता दस्तावेज (नैदानिक संदेश जारी करने के साथ या बिना) के व्यवहार के साथ होता है, अनुवाद या निष्पादन को समाप्त करने के लिए (के साथ) एक नैदानिक संदेश जारी करना)।
तो एक कंपाइलर को आपके प्रोग्राम को संकलन समय पर अस्वीकार करने की अनुमति दी जाती है यदि उसका व्यवहार अपरिभाषित है। लेकिन मेरी व्याख्या यह है कि यह केवल तभी कर सकता है जब यह साबित कर सकता है कि कार्यक्रम का हर निष्पादन अपरिभाषित व्यवहार का सामना करेगा। जिसका अर्थ है, मुझे लगता है कि यह:
if (rand() % 2 == 0) {
i = i / 0;
}
जो निश्चित रूप से कर सकते हैं अपरिभाषित व्यवहार है, संकलन समय पर अस्वीकार नहीं किया जा सकता है।
एक व्यावहारिक मामले के रूप में, अपरिभाषित व्यवहार को रोकने के लिए कार्यक्रमों को रनटाइम परीक्षण करने में सक्षम होना चाहिए, और मानक को उन्हें ऐसा करने की अनुमति देनी होगी।
आपका उदाहरण था:
if (0) {
i = 1/0;
}
जो कभी भी डिवीजन को 0. निष्पादित नहीं करता है: एक बहुत ही सामान्य मुहावरा है:
int x, y;
if (y != 0) {
x = x / y;
}
विभाजन निश्चित रूप से अपरिभाषित व्यवहार है अगर y == 0
, लेकिन यह कभी भी निष्पादित नहीं होता है y == 0
। व्यवहार अच्छी तरह से परिभाषित है, और उसी कारण से कि आपका उदाहरण अच्छी तरह से परिभाषित है: क्योंकि क्षमता अपरिभाषित व्यवहार वास्तव में कभी नहीं हो सकता है।
(जब तक INT_MIN < -INT_MAX && x == INT_MIN && y == -1
(हाँ, पूर्णांक विभाजन ओवरफ्लो हो सकता है), लेकिन यह एक अलग मुद्दा है।)
एक टिप्पणी में (हटाए जाने के बाद), किसी ने बताया कि संकलक संकलन समय पर निरंतर अभिव्यक्तियों का मूल्यांकन कर सकता है। यह सच है, लेकिन इस मामले में प्रासंगिक नहीं है, क्योंकि के संदर्भ में
i = 1/0;
1/0
एक स्थिर अभिव्यक्ति नहीं है ।
एक स्थिर-अभिव्यक्ति एक वाक्यात्मक श्रेणी है जो सशर्त-अभिव्यक्ति (जो असाइनमेंट और अल्पविराम अभिव्यक्तियों को छोड़कर ) को कम करती है । उत्पादन निरंतर-अभिव्यक्ति व्याकरण में केवल संदर्भों में प्रकट होती है, जिन्हें वास्तव में एक स्थिर अभिव्यक्ति की आवश्यकता होती है, जैसे कि केस लेबल। इसलिए यदि आप लिखते हैं:
switch (...) {
case 1/0:
...
}
तब 1/0
एक स्थिर अभिव्यक्ति है - और 6.6p4 में एक बाधा का उल्लंघन करता है: "प्रत्येक स्थिर अभिव्यक्ति एक ऐसे निरंतर का मूल्यांकन करेगी जो उसके प्रकार के लिए प्रतिनिधित्व योग्य मूल्यों की सीमा में है।", इसलिए एक निदान की आवश्यकता है। लेकिन एक असाइनमेंट के दाहिने हाथ की ओर एक स्थिर-अभिव्यक्ति की आवश्यकता नहीं है , केवल एक सशर्त-अभिव्यक्ति है , इसलिए निरंतर अभिव्यक्तियों पर बाधाएं लागू नहीं होती हैं। एक संकलक किसी भी अभिव्यक्ति का मूल्यांकन कर सकता है जो कि संकलन के समय में सक्षम है, लेकिन केवल तभी व्यवहार समान है जब इसे निष्पादन के दौरान मूल्यांकन किया गया था (या, संदर्भ में if (0)
, निष्पादन के दौरान मूल्यांकन नहीं किया गया है)।
(कुछ ऐसा जो वास्तव में एक जैसी लगती है निरंतर अभिव्यक्ति जरूरी नहीं कि एक है निरंतर अभिव्यक्ति , बस के रूप में, में x + y * z
, अनुक्रम x + y
एक नहीं है additive अभिव्यक्ति संदर्भ में यह प्रतीत होता है की वजह से।)
जिसका अर्थ है N1570 खंड 6.6 में फुटनोट जो मैं उद्धृत करने जा रहा था:
इस प्रकार, निम्नलिखित आरंभीकरण में,
static int i = 2 || 1 / 0;
भाव एक मान के साथ एक मान्य पूर्णांक स्थिर अभिव्यक्ति है।
वास्तव में इस प्रश्न के लिए प्रासंगिक नहीं है।
अंत में, कुछ चीजें हैं जिन्हें अपरिभाषित व्यवहार के कारण परिभाषित किया जाता है जो कि निष्पादन के दौरान क्या होता है। अनुलग्नक जे, सी मानक के खंड 2 (फिर से, एन 1570 ड्राफ्ट देखें ) उन चीजों को सूचीबद्ध करता है जो अपरिभाषित व्यवहार का कारण बनता है, बाकी मानक से इकट्ठा किया जाता है। कुछ उदाहरण (मेरा दावा है कि यह एक विस्तृत सूची नहीं है) हैं:
- एक गैर-रिक्त स्रोत फ़ाइल एक नई-पंक्ति वर्ण में समाप्त नहीं होती है, जो तुरंत बैकस्लैश वर्ण से पहले नहीं होती है या आंशिक प्रीप्रोसेसिंग टोकन या टिप्पणी में समाप्त होती है
- टोकन संयोजन एक सार्वभौमिक अनुक्रम नाम के वाक्यविन्यास से मेल खाता एक चरित्र अनुक्रम पैदा करता है
- मूल स्रोत वर्ण सेट में नहीं एक चरित्र एक स्रोत फ़ाइल में पाया जाता है, एक पहचानकर्ता, एक चरित्र निरंतर, एक स्ट्रिंग शाब्दिक, एक हेडर नाम, एक टिप्पणी, या एक पूर्व टोकन के रूप में जो कभी टोकन में परिवर्तित नहीं होता है।
- एक पहचानकर्ता, टिप्पणी, स्ट्रिंग शाब्दिक, चरित्र स्थिर, या हेडर नाम में एक अमान्य मल्टीबाइट चरित्र होता है या प्रारंभिक पारी की स्थिति में शुरू और समाप्त नहीं होता है
- एक ही पहचानकर्ता का एक ही अनुवाद इकाई में आंतरिक और बाहरी दोनों तरह का जुड़ाव होता है
ये विशेष मामले ऐसी चीजें हैं जो एक संकलक का पता लगा सकता है। मुझे लगता है कि उनका व्यवहार अपरिभाषित है क्योंकि समिति सभी कार्यान्वयनों पर समान व्यवहार लागू नहीं करना चाहती थी, या नहीं कर सकती थी, और अनुमत व्यवहारों की एक श्रृंखला को परिभाषित करना सिर्फ प्रयास के लायक नहीं था। वे वास्तव में "कोड जिसे कभी निष्पादित नहीं किया जाएगा" की श्रेणी में नहीं आते हैं, लेकिन मैं उन्हें पूर्णता के लिए यहां उल्लेख करता हूं।