c++ अपरिभाषित व्यवहार और अनुक्रम बिंदु




undefined-behavior c++-faq (4)

"अनुक्रम बिंदु" क्या हैं?

अपरिभाषित व्यवहार और अनुक्रम बिंदुओं के बीच संबंध क्या है?

मैं अक्सर मजाकिया और घुलनशील अभिव्यक्तियों का उपयोग करता हूं जैसे a[++i] = i; , खुद को बेहतर महसूस करने के लिए। मुझे उनका उपयोग क्यों करना बंद कर देना चाहिए?

यदि आपने इसे पढ़ लिया है, तो फॉलो-अप प्रश्न अपरिभाषित व्यवहार और अनुक्रम बिंदुओं को पुनः लोड करने के लिए सुनिश्चित रहें

(नोट: यह स्टैक ओवरफ्लो के सी ++ एफएक्यू में प्रवेश करने के लिए है । यदि आप इस फॉर्म में एक एफएक्यू प्रदान करने के विचार की आलोचना करना चाहते हैं, तो मेटा पर पोस्ट करना जो यह सब शुरू कर देगा, ऐसा करने का स्थान होगा। उस प्रश्न की निगरानी सी ++ चैटरूम में की जाती है, जहां एफएक्यू विचार पहली जगह शुरू हुआ था, इसलिए आपके उत्तर को उन लोगों द्वारा पढ़ा जाने की संभावना है जो इस विचार के साथ आए थे।)


सी ++ 17 ( N4659 9) में एक प्रस्ताव शामिल है जिसमें N4659 सी ++ के लिए अभिव्यक्ति अभिव्यक्ति मूल्यांकन आदेश शामिल है जो अभिव्यक्ति मूल्यांकन के एक कठोर क्रम को परिभाषित करता है।

विशेष रूप से, निम्नलिखित वाक्य जोड़ा गया था:

8.18 असाइनमेंट और कंपाउंड असाइनमेंट ऑपरेटर :
....

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

यह पहले अनिर्धारित व्यवहार के कई मामलों को वैध बनाता है, जिसमें एक प्रश्न भी शामिल है:

a[++i] = i;

हालांकि कई अन्य समान मामले अभी भी अपरिभाषित व्यवहार का कारण बनते हैं।

N4140 :

i = i++ + 1; // the behavior is undefined

लेकिन N4659

i = i++ + 1; // the value of i is incremented
i = i++ + i; // the behavior is undefined

बेशक, एक सी ++ 17 अनुपालन कंपाइलर का उपयोग करने का मतलब यह नहीं है कि किसी को ऐसे अभिव्यक्ति लिखना शुरू करना चाहिए।


यह मेरे पिछले उत्तर का अनुवर्ती है और इसमें C ++ 11 संबंधित सामग्री शामिल है।

पूर्व-आवश्यकताएं : संबंधों का एक प्राथमिक ज्ञान (गणित)।

क्या यह सच है कि सी ++ 11 में कोई अनुक्रम अंक नहीं हैं?

हाँ! यह एकदम सच है।

अनुक्रम अंक को सी ++ 11 में relations बाद अनुक्रमित और अनुक्रमित (और बाद में और अनिश्चित रूप से अनुक्रमित ) द्वारा प्रतिस्थापित किया गया है।

यह 'चीज़ से पहले अनुक्रमित' वास्तव में क्या है?

पहले (§1.9 / 13) अनुक्रमित एक संबंध है जो है:

एक thread द्वारा निष्पादित मूल्यांकन के बीच और एक सख्त आंशिक आदेश 1 प्रेरित करता है

औपचारिक रूप से इसका मतलब है कि किसी भी दो मूल्यांकन (नीचे देखें) A और B , यदि A को B से पहले अनुक्रमित किया गया है , तो A के निष्पादन B के निष्पादन से पहले होगा । यदि A और B से पहले अनुक्रमित नहीं किया गया है तो A पहले अनुक्रमित नहीं किया गया है, तो A और B का अपूर्ण नहीं है 2

मूल्यांकन A और B को अनिश्चित रूप से अनुक्रमित किया जाता है जब A या B को B से पहले अनुक्रमित किया जाता है, लेकिन यह अनिर्दिष्ट है कि 3

[टिप्पणियाँ]
1: एक सख्त आंशिक क्रम एक सेट P पर एक द्विआधारी संबंध "<" जो Asymmetric , और Transitive , यानी, P में सभी a , b , और c , हमारे पास यह है:
........(मैं)। यदि एक <बी तो ¬ (बी <ए) ( asymmetry );
........ (ii)। अगर एक <बी और बी <सी तो एक <सी ( transitivity )।
2: अपरिचित मूल्यांकन का निष्पादन ओवरलैप हो सकता है
3: अनिश्चित रूप से अनुक्रमित मूल्यांकन ओवरलैप नहीं हो सकते हैं , लेकिन पहले इसे निष्पादित किया जा सकता है।

सी ++ 11 के संदर्भ में 'मूल्यांकन' शब्द का अर्थ क्या है?

सी ++ 11 में, सामान्य रूप से एक अभिव्यक्ति (या उप-अभिव्यक्ति) का मूल्यांकन शामिल है:

अब (§1.9 / 14) कहता है:

पूर्ण मूल्य अभिव्यक्ति से जुड़े प्रत्येक मूल्य गणना और साइड इफेक्ट का मूल्यांकन प्रत्येक मूल्य गणना और अगली पूर्ण अभिव्यक्ति के साथ साइड इफेक्ट से पहले किया जाना चाहिए

  • मामूली उदाहरण:

    int x; x = 10; ++x;

    मूल्य गणना और ++x से जुड़े दुष्प्रभाव को मान गणना और x = 10; दुष्प्रभाव के बाद अनुक्रमित किया गया है x = 10;

तो अपरिभाषित व्यवहार और उपर्युक्त चीजों के बीच कुछ संबंध होना चाहिए, है ना?

हाँ! सही।

(§1.9 / 15) में इसका उल्लेख किया गया है

जहां भी ध्यान दिया गया है, अलग-अलग ऑपरेटरों के संचालन और व्यक्तिगत अभिव्यक्तियों के उप-अभिव्यक्तियों के मूल्यांकन के परिणामस्वरूप 4 नहीं हैं

उदाहरण के लिए :

int main()
{
     int num = 19 ;
     num = (num << 3) + (num >> 3);
} 
  1. + ऑपरेटर के संचालन का मूल्यांकन एक-दूसरे के सापेक्ष अपरिचित है।
  2. << और >> ऑपरेटरों के संचालन का मूल्यांकन एक-दूसरे के सापेक्ष अपरिचित है।

4: एक अभिव्यक्ति में जिसे किसी प्रोग्राम के निष्पादन के दौरान एक से अधिक बार मूल्यांकन किया जाता है, इसके उप- अभिव्यक्तियों के अपरिचित और अनिश्चित रूप से अनुक्रमित मूल्यांकनों को विभिन्न मूल्यांकनों में निरंतर प्रदर्शन नहीं किया जाना चाहिए।

(§1.9 / 15) ऑपरेटर के संचालन के मूल्य गणना से पहले ऑपरेटर के ऑपरेटरों की मान गणना अनुक्रमित होती है।

इसका मतलब x + y में x और y की मान गणना गणना (x + y) की मान गणना से पहले अनुक्रमित होती है।

अधिक महत्वपूर्ण बात

(§1.9 / 15) यदि स्केलर ऑब्जेक्ट पर साइड इफेक्ट किसी भी रिश्तेदार के विपरीत नहीं है

(ए) एक ही स्केलर ऑब्जेक्ट पर एक और दुष्प्रभाव

या

(बी) एक ही स्केलर ऑब्जेक्ट के मान का उपयोग करके मूल्य गणना।

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

उदाहरण:

int i = 5, v[10] = { };
void  f(int,  int);
  1. i = i++ * ++i; // Undefined Behaviour
  2. i = ++i + i++; // Undefined Behaviour
  3. i = ++i + ++i; // Undefined Behaviour
  4. i = v[i++]; // Undefined Behaviour
  5. i = v[++i]: // Well-defined Behavior
  6. i = i++ + 1; // Undefined Behaviour
  7. i = ++i + 1; // Well-defined Behaviour
  8. ++++i; // Well-defined Behaviour
  9. f(i = -1, i = -1); // Undefined Behaviour (see below)

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

अभिव्यक्तियां (5) , (7) और (8) अपरिभाषित व्यवहार का आह्वान न करें। अधिक विस्तृत स्पष्टीकरण के लिए निम्नलिखित उत्तरों देखें।

अंतिम नोट :

यदि आपको पोस्ट में कोई दोष मिलता है तो कृपया एक टिप्पणी छोड़ दें। पावर-उपयोगकर्ता (प्रतिनिधि> 20000 के साथ) कृपया टाइपो और अन्य गलतियों को ठीक करने के लिए पोस्ट को संपादित करने में संकोच न करें।


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

f (a,b)

पहले या तो एक बी, या, बी तो ए। अब, ए और बी का मूल्यांकन अंतःस्थापित या यहां तक ​​कि विभिन्न कोरों पर भी किया जा सकता है।


सी ++ 98 और सी ++ 03

यह उत्तर C ++ मानक के पुराने संस्करणों के लिए है। मानक के सी ++ 11 और सी ++ 14 संस्करण औपचारिक रूप से 'अनुक्रम बिंदु' नहीं होते हैं; संचालन 'अनुक्रमित' या 'अपूर्ण' या 'अनिश्चित रूप से अनुक्रमित' हैं। शुद्ध प्रभाव अनिवार्य रूप से वही है, लेकिन शब्दावली अलग है।

अस्वीकरण : ठीक है। यह जवाब थोड़ा लंबा है। तो इसे पढ़ने के दौरान धैर्य रखें। यदि आप पहले से ही इन चीजों को जानते हैं, तो उन्हें फिर से पढ़ना आपको पागल नहीं बनायेगा।

पूर्व-आवश्यकताएं : सी ++ मानक का प्राथमिक ज्ञान

अनुक्रम अंक क्या हैं?

मानक कहते हैं

निष्पादन अनुक्रम में कुछ निर्दिष्ट बिंदुओं पर अनुक्रम बिंदु कहा जाता है , पिछले मूल्यांकन के सभी दुष्प्रभाव पूर्ण हो जाएंगे और बाद के मूल्यांकनों का कोई दुष्प्रभाव नहीं होगा। (§1.9 / 7)

दुष्प्रभाव? दुष्प्रभाव क्या हैं?

अभिव्यक्ति का मूल्यांकन कुछ उत्पन्न करता है और यदि इसके अलावा निष्पादन पर्यावरण की स्थिति में कोई बदलाव होता है तो यह कहा जाता है कि अभिव्यक्ति (इसके मूल्यांकन) के कुछ दुष्प्रभाव होते हैं।

उदाहरण के लिए:

int x = y++; //where y is also an int

प्रारंभिक ऑपरेशन के अलावा ++ ऑपरेटर के साइड इफेक्ट के कारण y का मान बदल जाता है।

अब तक सब ठीक है। अनुक्रम बिंदुओं पर आगे बढ़ना। Comp.lang.c लेखक Steve Summit द्वारा दिए गए सीक-पॉइंट की एक वैकल्पिक परिभाषा:

अनुक्रम बिंदु उस बिंदु पर एक बिंदु है जिस पर धूल बस गया है और अब तक देखे गए सभी दुष्प्रभावों को पूरा होने की गारंटी है।

सी ++ मानक में सूचीबद्ध सामान्य अनुक्रम बिंदु क्या हैं?

वो है:

  • पूर्ण अभिव्यक्ति के मूल्यांकन के अंत में ( §1.9/16 ) (एक पूर्ण अभिव्यक्ति एक अभिव्यक्ति है जो किसी अन्य अभिव्यक्ति का उप-अभिव्यक्ति नहीं है।) 1

उदाहरण :

int a = 5; // ; is a sequence point here
  • पहली अभिव्यक्ति ( §1.9/18 ) 2 के मूल्यांकन के बाद निम्नलिखित में से प्रत्येक अभिव्यक्ति के मूल्यांकन में

    • a && b (§5.14)
    • a || b (§5.15)
    • a ? b : c (§5.16)
    • a , b (§5.18) (यहां ए, बी एक अल्पविराम ऑपरेटर है; func(a,a++) , अल्पविराम ऑपरेटर नहीं है, यह तर्क और a++ बीच केवल एक विभाजक है। इस प्रकार व्यवहार उस मामले में अपरिभाषित है (यदि a आदिम प्रकार माना जाता है))
  • फ़ंक्शन बॉडी ( §1.9/17 ) में किसी भी अभिव्यक्ति या कथन के निष्पादन से पहले सभी फ़ंक्शन तर्क (यदि कोई हो) के मूल्यांकन के बाद फ़ंक्शन कॉल (चाहे फ़ंक्शन इनलाइन है) पर।

1: नोट: पूर्ण अभिव्यक्ति के मूल्यांकन में उप-अभिव्यक्तियों का मूल्यांकन शामिल हो सकता है जो पूर्ण अभिव्यक्ति का स्पष्ट रूप से हिस्सा नहीं हैं। उदाहरण के लिए, डिफ़ॉल्ट तर्क अभिव्यक्तियों (8.3.6) का मूल्यांकन करने में शामिल उप-अभिव्यक्तियों को अभिव्यक्ति में बनाया जाता है जो फ़ंक्शन को कॉल करता है, अभिव्यक्ति नहीं जो डिफ़ॉल्ट तर्क को परिभाषित करता है

2: संकेतक संकेतित हैं कि खंड 5 में वर्णित अंतर्निहित ऑपरेटर हैं। जब इन ऑपरेटरों में से एक वैध संदर्भ में अधिभारित (खंड 13) है, इस प्रकार उपयोगकर्ता द्वारा परिभाषित ऑपरेटर फ़ंक्शन को निर्दिष्ट करते हुए, अभिव्यक्ति फ़ंक्शन आमंत्रण को निर्दिष्ट करती है और ऑपरेंड एक तर्क सूची बनाते हैं, उनके बीच एक अंतर्निहित अनुक्रम बिंदु के बिना।

अपरिभाषित व्यवहार क्या है?

मानक धारा §1.3.12 रूप में अपरिभाषित व्यवहार को परिभाषित करता है

व्यवहार, जैसे किसी गलत प्रोग्राम निर्माण या गलत डेटा के उपयोग पर उत्पन्न हो सकता है, जिसके लिए यह अंतर्राष्ट्रीय मानक कोई आवश्यकता नहीं लगाता है 3

अनिश्चित व्यवहार की भी उम्मीद की जा सकती है जब यह अंतर्राष्ट्रीय मानक व्यवहार की किसी भी स्पष्ट परिभाषा के वर्णन को छोड़ देता है।

3: अनुमोदित अपरिभाषित व्यवहार, अप्रत्याशित परिणामों के साथ पूरी तरह से स्थिति को अनदेखा करने से, अनुवाद या निष्पादन को समाप्त करने के लिए पर्यावरण की विशेषता (एक नैदानिक ​​संदेश जारी करने के साथ) के दस्तावेज तरीके से अनुवाद या प्रोग्राम निष्पादन के दौरान व्यवहार करने के लिए होता है। (एक नैदानिक ​​संदेश जारी करने के साथ)।

संक्षेप में, अपरिभाषित व्यवहार का मतलब है कि आपकी नाक से बाहर निकलने वाले डेमन्स से आपकी गर्लफ्रेंड गर्भवती होने पर कुछ भी हो सकता है।

अपरिभाषित व्यवहार और अनुक्रम अंक के बीच संबंध क्या है?

इससे पहले कि आप इसमें शामिल हों, आपको अपरिभाषित व्यवहार, अनिर्दिष्ट व्यवहार और कार्यान्वयन परिभाषित व्यवहार के बीच अंतर (ओं) पता होना चाहिए।

आपको यह भी पता होना चाहिए कि the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified

उदाहरण के लिए:

int x = 5, y = 6;

int z = x++ + y++; //it is unspecified whether x++ or y++ will be evaluated first.

here एक और उदाहरण।

अब §5/4 में मानक कहते हैं

  • 1) पिछले और अगले अनुक्रम बिंदु के बीच एक स्केलर ऑब्जेक्ट में अभिव्यक्ति के मूल्यांकन द्वारा एक बार में संग्रहीत मूल्य संशोधित किया जाएगा।

इसका क्या मतलब है?

अनौपचारिक रूप से इसका मतलब है कि दो अनुक्रम बिंदुओं के बीच एक चर को एक से अधिक बार संशोधित नहीं किया जाना चाहिए। एक अभिव्यक्ति कथन में, next sequence point आमतौर पर समाप्ति अर्धविराम पर होता है, और previous sequence point पिछले कथन के अंत में होता है। एक अभिव्यक्ति में मध्यवर्ती sequence points भी हो सकते हैं।

उपरोक्त वाक्य से निम्नलिखित अभिव्यक्ति अनिर्धारित व्यवहार का आह्वान करते हैं:

i++ * ++i;   // UB, i is modified more than once btw two SPs
i = ++i;     // UB, same as above
++i = 2;     // UB, same as above
i = ++i + 1; // UB, same as above
++++++i;     // UB, parsed as (++(++(++i)))

i = (i, ++i, ++i); // UB, there's no SP between `++i` (right most) and assignment to `i` (`i` is modified more than once btw two SPs)

लेकिन निम्नलिखित अभिव्यक्ति ठीक हैं:

i = (i, ++i, 1) + 1; // well defined (AFAIK)
i = (++i, i++, i);   // well defined 
int j = i;
j = (++i, i++, j*i); // well defined
  • 2) इसके अलावा, पूर्व मान केवल संग्रहीत करने के मूल्य को निर्धारित करने के लिए उपयोग किया जाएगा।

इसका क्या मतलब है? इसका अर्थ यह है कि यदि किसी ऑब्जेक्ट को पूर्ण अभिव्यक्ति के भीतर लिखा गया है, तो किसी भी अभिव्यक्ति के भीतर किसी भी और सभी तक पहुंच प्राप्त की जानी चाहिए, जो कि लिखे जाने वाले मान की गणना में सीधे शामिल होना चाहिए

उदाहरण के लिए i = i + 1 सभी की पहुंच (एलएचएस और आरएचएस में) सीधे लिखे जाने वाले मान की गणना में शामिल होती है। तो यह ठीक है।

यह नियम प्रभावी रूप से उन लोगों के लिए कानूनी अभिव्यक्तियों को बाधित करता है जिनमें प्रदर्शन के पहले प्रदर्शन से पहले पहुंच होती है।

उदाहरण 1:

std::printf("%d %d", i,++i); // invokes Undefined Behaviour because of Rule no 2

उदाहरण 2:

a[i] = i++ // or a[++i] = i or a[i++] = ++i etc

अस्वीकृत है क्योंकि i ( i a[i] में से a[i] ) के मूल्य में से किसी एक के साथ कुछ भी नहीं करना है जो i (जो i++ में होता है) में संग्रहीत होता है, और इसलिए परिभाषित करने का कोई अच्छा तरीका नहीं है-- या तो हमारी समझ या संकलक के लिए - क्या बढ़ी हुई मान को संग्रहीत करने से पहले या उसके बाद पहुंच होनी चाहिए। तो व्यवहार अपरिभाषित है।

उदाहरण 3:

int x = i + i++ ;// Similar to above

here जवाब here पालन here





sequence-points