अपरिभाषित व्यवहार और अनुक्रम बिंदु पुनः लोड किए गए


84

इस विषय पर निम्नलिखित विषय की अगली कड़ी पर विचार करें:

पिछली किस्त
अपरिभाषित व्यवहार और अनुक्रम बिंदु

आइए इस मज़ेदार और जटिल अभिव्यक्ति को फिर से देखें (इटैलिक किए गए वाक्यांश उपरोक्त विषय * मुस्कान * से लिए गए हैं):

i += ++i;

हम कहते हैं कि यह अपरिभाषित व्यवहार को आमंत्रित करता है। मुझे लगता है कि जब यह कहते हैं, हम परोक्ष कि मान प्रकार का iबिल्ट-इन प्रकारों में से एक है।

क्या होगा यदि प्रकार के iउपयोगकर्ता-निर्धारित प्रकार है? इसका प्रकार कहें Indexजो इस पोस्ट में बाद में परिभाषित किया गया है (नीचे देखें)। क्या यह अभी भी अपरिभाषित व्यवहार को लागू करेगा?

यदि हाँ, तो क्यों? क्या यह लेखन के बराबर नहीं है i.operator+=(i.operator++());या यहां तक ​​कि वाक्य-रचना भी सरल है i.add(i.inc());? या, क्या वे भी अपरिभाषित व्यवहार करते हैं?

यदि नहीं, तो क्यों नहीं? आखिरकार, ऑब्जेक्ट लगातार अनुक्रम बिंदुओं के बीच दो बारi संशोधित हो जाता है । कृपया अंगूठे के नियम को याद रखें: एक अभिव्यक्ति लगातार "अनुक्रम बिंदुओं के बीच केवल एक बार एक वस्तु के मूल्य को संशोधित कर सकती है । और यदि एक अभिव्यक्ति है, तो उसे अपरिभाषित व्यवहार को आमंत्रित करना चाहिए। यदि ऐसा है, तो इसके समकक्षों और अपरिभाषित व्यवहार को भी आमंत्रित करना चाहिए। असत्य लगता है! (जहाँ तक मैं समझता हूँ)i += ++ii.operator+=(i.operator++());i.add(i.inc());

या, शुरू करने के लिए i += ++iएक अभिव्यक्ति नहीं है ? यदि हां, तो यह क्या है और अभिव्यक्ति की परिभाषा क्या है ?

यदि यह एक अभिव्यक्ति है, और एक ही समय में, इसका व्यवहार भी अच्छी तरह से परिभाषित है, तो इसका मतलब है कि किसी अभिव्यक्ति से जुड़े अनुक्रम बिंदुओं की संख्या अभिव्यक्ति में शामिल ऑपरेंड के प्रकार पर निर्भर करती है । क्या मैं सही हूं (आंशिक रूप से भी)?


वैसे, इस अभिव्यक्ति के बारे में कैसे?

//Consider two cases:
//1. If a is an array of a built-in type
//2. If a is user-defined type which overloads the subscript operator!

a[++i] = i; //Taken from the previous topic. But here type of `i` is Index.

आपको अपनी प्रतिक्रिया में भी इस पर विचार करना चाहिए (यदि आप इसके व्यवहार को जानते हैं)। :-)


है

++++++i;

C ++ 03 में अच्छी तरह से परिभाषित? आखिरकार, यह यह है,

((i.operator++()).operator++()).operator++();

class Index
{
    int state;

    public:
        Index(int s) : state(s) {}
        Index& operator++()
        {
            state++;
            return *this;
        }
        Index& operator+=(const Index & index)
        {
            state+= index.state;
            return *this;
        }
        operator int()
        {
            return state;
        }
        Index & add(const Index & index)
        {
            state += index.state;
            return *this;
        }
        Index & inc()
        {
            state++;
            return *this;
        }
};

13
+1 महान प्रश्न, जिसने महान उत्तरों को प्रेरित किया। मुझे लगता है कि मुझे यह कहना चाहिए कि यह अभी भी भयानक कोड है जिसे अधिक पठनीय होने के लिए फिर से तैयार किया जाना चाहिए, लेकिन आप शायद जानते हैं कि वैसे भी :)
फिलिप पॉटर

4
@ प्रश्न यह है: किसने कहा कि यह समान है? या किसने कहा कि यह समान नहीं है? क्या यह इस बात पर निर्भर नहीं करता है कि आप उन्हें कैसे लागू करते हैं? (नोट: मैं sउपयोगकर्ता-परिभाषित प्रकार मान रहा हूं !)
नवाज

5
मैं किसी भी अदिश वस्तु को दो अनुक्रम बिंदुओं के बीच दो बार संशोधित नहीं करता ...
जोहान्स स्काउब -

3
@ जोहान्स: फिर यह स्केलर ऑब्जेक्ट के बारे में है। यह क्या है? मुझे आश्चर्य है कि मैंने इसके बारे में पहले कभी क्यों नहीं सुना। हो सकता है, क्योंकि ट्यूटोरियल / सी ++ - faq इसका उल्लेख नहीं करते हैं, या इसे जोर नहीं देते हैं? क्या यह अंतर्निहित प्रकार की वस्तुओं से अलग है ?
नवाज

3
@Phillip: जाहिर है, मैं वास्तविक जीवन में ऐसा कोड लिखने वाला नहीं हूं; वास्तव में, कोई भी संत प्रोग्रामर इसे लिखने वाला नहीं है। इन सवालों को आमतौर पर तैयार किया जाता है ताकि हम अपरिभाषित व्यवहार और अनुक्रम बिंदुओं के पूरे व्यवसाय को बेहतर ढंग से समझ सकें! :-)
नवाज

जवाबों:


48

यह कोड की तरह दिखता है

i.operator+=(i.operator ++());

अनुक्रम बिंदुओं के संबंध में पूरी तरह से ठीक काम करता है। सी ++ आईएसओ मानक की धारा 1.9.17 अनुक्रम बिंदुओं और फ़ंक्शन मूल्यांकन के बारे में यह कहती है:

किसी फ़ंक्शन को कॉल करना (फ़ंक्शन फ़ंक्शन इनलाइन है या नहीं), सभी फ़ंक्शन तर्कों (यदि कोई है) के मूल्यांकन के बाद एक अनुक्रम बिंदु है जो फ़ंक्शन बॉडी में किसी भी अभिव्यक्तियों या बयानों के निष्पादन से पहले होता है। फ़ंक्शन के बाहर किसी भी भाव के निष्पादन से पहले और लौटे मूल्य की नकल के बाद एक अनुक्रम बिंदु भी है।

यह इंगित करेगा, उदाहरण के लिए, कि मूल्यांकन के बाद अनुक्रम बिंदु के i.operator ++()रूप में पैरामीटर operator +=। संक्षेप में, क्योंकि अतिभारित ऑपरेटर कार्य हैं, सामान्य अनुक्रमण नियम लागू होते हैं।

बढ़िया सवाल, वैसे! मुझे वास्तव में पसंद है कि आप कैसे मुझे उस भाषा की सभी बारीकियों को समझने के लिए मजबूर कर रहे हैं जो मैंने पहले ही सोचा था कि मैं जानता था (और सोचा था कि मुझे लगा कि मुझे पता है)। :-)


12

http://www.eelis.net/C+/analogliterals.xhtml एनालॉग शाब्दिक शब्द मेरे दिमाग में आते हैं

  unsigned int c = ( o-----o
                     |     !
                     !     !
                     !     !
                     o-----o ).area;

  assert( c == (I-----I) * (I-------I) );

  assert( ( o-----o
            |     !
            !     !
            !     !
            !     !
            o-----o ).area == ( o---------o
                                |         !
                                !         !
                                o---------o ).area );

एक सवाल था, ++++++ i है; C ++ 03 में अच्छी तरह से परिभाषित?
इंडस्ट्रियल-एंटीडिप्रेसेंट

11

जैसा कि अन्य लोगों ने कहा है, आपका i += ++iउदाहरण उपयोगकर्ता-परिभाषित प्रकार के साथ काम करता है क्योंकि आप फ़ंक्शन कॉल कर रहे हैं, और फ़ंक्शन में अनुक्रम बिंदु शामिल हैं।

दूसरी ओर, a[++i] = iयह इतना भाग्यशाली नहीं है कि aयह माना जाता है कि आपका मूल सरणी प्रकार है, या यहां तक ​​कि एक उपयोगकर्ता भी परिभाषित है। यहाँ आपको जो समस्या है वह यह है कि हमें पता नहीं है कि अभिव्यक्ति के किस भाग का iमूल्यांकन पहले किया गया है। ऐसा हो सकता है कि वहां वस्तु को पुनः प्राप्त करने के लिए ++iमूल्यांकन operator[](या कच्चे संस्करण) को पारित कर दिया जाए , और फिर उस मूल्य को iपास कर दिया जाए (जो कि उसके बाद मैं बढ़ाया गया था)। दूसरी ओर, शायद बाद के पक्ष का मूल्यांकन पहले किया जाता है, बाद में असाइनमेंट के लिए संग्रहीत किया जाता है, और फिर ++iभाग का मूल्यांकन किया जाता है।


इसलिए ... परिणाम यूबी के बजाय अनिर्दिष्ट है, क्योंकि अभिव्यक्ति के मूल्यांकन का क्रम अनिर्दिष्ट है?
फिलिप पॉटर

@Philip: अनिर्दिष्ट का अर्थ है कि हम संकलक से व्यवहार को निर्दिष्ट करने की अपेक्षा करते हैं, जबकि अपरिभाषित स्थानों पर ऐसा कोई दायित्व नहीं है। मुझे लगता है कि यह अनुकूलन के लिए अधिक कमरे में जाने के लिए, यहां अपरिभाषित है।
मैथ्यू एम।

@ नोहा: मैंने भी एक प्रतिक्रिया पोस्ट की। कृपया इसे देखें, और मुझे अपने विचार बताएं। :-)
नवाज

1
@Philip: परिणाम UB है, क्योंकि नियम 5/4 में है: "इस पैराग्राफ की आवश्यकताओं को पूर्ण अभिव्यक्ति के उप-क्रम के प्रत्येक स्वीकार्य आदेश के लिए पूरा किया जाएगा; अन्यथा व्यवहार अपरिभाषित है।" यदि सभी स्वीकार्य आदेशों में संशोधन ++i, और iअसाइनमेंट के आरएचएस पर पढ़ने के बीच अनुक्रम बिंदु थे , तो आदेश अनिर्दिष्ट होगा। क्योंकि स्वीकार्य आदेशों में से एक उन दो चीजों को करता है जिनमें कोई हस्तक्षेप करने वाला अनुक्रम बिंदु नहीं होता है, व्यवहार अपरिभाषित होता है।
स्टीव जेसोप

1
@Pipip: यह अनिर्दिष्ट व्यवहार के रूप में अनिर्दिष्ट व्यवहार को परिभाषित नहीं करता है। फिर, यदि अनिर्दिष्ट व्यवहार की श्रेणी में कुछ शामिल हैं, जो अपरिभाषित है, तो समग्र व्यवहार अपरिभाषित है। यदि अनिर्दिष्ट व्यवहार की सीमा को सभी संभावनाओं में परिभाषित किया गया है, तो समग्र व्यवहार अनिर्दिष्ट है। लेकिन आप दूसरे बिंदु पर सही हैं, मैं उपयोगकर्ता-परिभाषित aऔर बिल्डिन के बारे में सोच रहा था i
स्टीव जेसोप

8

मुझे लगता है कि यह अच्छी तरह से परिभाषित है:

C ++ ड्राफ्ट मानक (n1905) से 161.9 / 16:

"रिटर्न वैल्यू की कॉपी करने और फ़ंक्शन 13 के बाहर किसी भी भाव के निष्पादन से पहले एक अनुक्रम बिंदु भी है)। C ++ में कई संदर्भ फ़ंक्शन फ़ंक्शन कॉल के मूल्यांकन का कारण बनते हैं, भले ही अनुवाद इकाई में कोई संबंधित फ़ंक्शन कॉल सिंटैक्स प्रकट नहीं होता है। [ उदाहरण : एक नई अभिव्यक्ति के मूल्यांकन में एक या अधिक आवंटन और निर्माता कार्यों का आह्वान; 5.3.4 देखना एक और उदाहरण के लिए, एक रूपांतरण समारोह (12.3.2) के आह्वान के संदर्भों में पैदा कर सकते जो कोई समारोह कॉल वाक्य रचना आइटम दिखाई देता है -।। अंत उदाहरण ] फंक्शन-एंट्री और फंक्शन-एग्ज़िट पर अनुक्रम अंक (जैसा कि ऊपर वर्णित है) मूल्यांकन के रूप में फ़ंक्शन कॉल की विशेषताएं हैं, जो भी अभिव्यक्ति का वाक्यविन्यास है।कि कॉल फ़ंक्शन हो सकता है। "

मैंने जिस हिस्से को बोल्ड किया, उस पर ध्यान दें। इसका मतलब है कि वृद्धि समारोह कॉल ( i.operator ++()) के बाद, लेकिन यौगिक असाइनमेंट कॉल ( i.operator+=) से पहले वास्तव में एक अनुक्रम बिंदु है ।


6

ठीक है। पिछली प्रतिक्रियाओं के माध्यम से जाने के बाद, मैंने अपने स्वयं के प्रश्न के बारे में फिर से सोचा, विशेष रूप से यह हिस्सा कि केवल नूह ने उत्तर देने का प्रयास किया, लेकिन मैं उसके साथ पूरी तरह से आश्वस्त नहीं हूं।

a[++i] = i;

मामला एक:

यदि aनिर्मित प्रकार की एक सरणी है। फिर नूह ने जो कहा वह सही है। अर्थात्,

[++ ++] = मैं इतना भाग्यशाली नहीं हूं कि यह मान लें कि आपका मूल सरणी प्रकार है, या यहां तक ​​कि एक उपयोगकर्ता एक परिभाषित किया । आपके यहाँ जो समस्या है वह यह है कि हमें पता नहीं है कि अभिव्यक्ति के किस भाग का मूल्यांकन पहले किया जाता है।

इसलिए a[++i]=iअपरिभाषित व्यवहार को आमंत्रित करता है, या परिणाम अनिर्दिष्ट है। जो भी है, यह अच्छी तरह से परिभाषित नहीं है!

पुनश्च: उपरोक्त उद्धरण में, हड़ताल के माध्यम से बेशक मेरा है।

केस 2:

यदि aउपयोगकर्ता-परिभाषित प्रकार का एक ऑब्जेक्ट है जो ओवरलोड करता है operator[], तो फिर से दो मामले हैं।

  1. यदि अतिभारित operator[]फ़ंक्शन का वापसी प्रकार अंतर्निहित प्रकार है, तो फिर से a[++i]=iअपरिभाषित व्यवहार को आमंत्रित करता है या परिणाम अनिर्दिष्ट होता है।
  2. लेकिन अगर अतिभारित operator[]फ़ंक्शन का वापसी प्रकार उपयोगकर्ता-परिभाषित प्रकार है, तो व्यवहार a[++i] = iको अच्छी तरह से परिभाषित किया गया है (जहां तक ​​मैं समझता हूं), क्योंकि इस मामले a[++i]=iमें लेखन के बराबर है a.operator[](++i).operator=(i);जो कि समान है a[++i].operator=(i);। यही है, असाइनमेंट लौटाए गए ऑब्जेक्ट operator=पर लागू होता है , जो बहुत अच्छी तरह से परिभाषित लगता है, क्योंकि समय के रिटर्न से, पहले से ही मूल्यांकन किया गया है, और फिर लौटे ऑब्जेक्ट कॉल फ़ंक्शन के अद्यतन मान को तर्क के रूप में पारित करते हैं । ध्यान दें कि इन दो कॉल के बीच एक अनुक्रम बिंदु है । और वाक्यविन्यास सुनिश्चित करता है कि इन दोनों कॉलों के बीच कोई प्रतिस्पर्धा नहीं है, औरa[++i]a[++i]++ioperator=ioperator[]पहले आह्वान किया जाएगा, और लगातार, इस तर्क को ++iपारित किया जाएगा, पहले भी मूल्यांकन किया जाएगा।

इसे ऐसे समझें someInstance.Fun(++k).Gun(10).Sun(k).Tun();जिसमें प्रत्येक लगातार फ़ंक्शन कॉल कुछ उपयोगकर्ता-परिभाषित प्रकार की वस्तु देता है। मेरे लिए, यह स्थिति इस तरह से अधिक लगती है: eat(++k);drink(10);sleep(k)क्योंकि दोनों स्थितियों में, प्रत्येक फ़ंक्शन कॉल के बाद अनुक्रम बिंदु मौजूद है।

कृपया मुझे सुधारें अगर मैं गलत हूं। :-)


1
@ नवाज़ k++और सीक्वेंस पॉइंट्स से अलग नहीं हुएk हैं । दोनों का मूल्यांकन या तो पहले किया जा सकता है या उनका मूल्यांकन किया जा सकता है । भाषा को केवल पहले मूल्यांकन करने की आवश्यकता है , न कि तर्क का मूल्यांकन करने से पहले । मैं संदर्भ देने में सक्षम हुए बिना एक ही बात को फिर से समझाने की कोशिश कर रहा हूं, इसलिए हम यहां से प्रगति नहीं करेंगे। SunFunFunSunFunSun
फिलिप पॉटर

1
@ नवाज़: क्योंकि ऐसा कुछ नहीं है जो उन्हें अलग करने वाले सीक्वेंस पॉइंट को परिभाषित करता हो। Sunनिष्पादित करने से पहले और बाद में अनुक्रम बिंदु हैं , लेकिन इससे पहले या बाद में Funतर्क ++kका मूल्यांकन किया जा सकता है। Funनिष्पादित करने से पहले और बाद में अनुक्रम बिंदु हैं , लेकिन इससे पहले या बाद में Sunतर्क kका मूल्यांकन किया जा सकता है। इसलिए, एक संभव मामले कि दोनों है kऔर ++kया तो पहले मूल्यांकन किया जाता है Sunया Funमूल्यांकन किया जाता है, और दोनों इतने समारोह-कॉल अनुक्रम अंक से पहले कर रहे हैं, और इसलिए कोई अनुक्रम बिंदु को अलग है kऔर ++k
फिलिप पॉटर

1
@Pipip: मैं दोहराता हूं: यह स्थिति कैसे अलग है eat(i++);drink(10);sleep(i);? ... अब भी, आप कह i++सकते हैं कि इससे पहले या उसके बाद मूल्यांकन किया जा सकता है?
नवाज

1
@ नवाज़: मैं खुद को और अधिक स्पष्ट कैसे कर सकता हूं? फन / सन उदाहरण में, के बीच और कोई अनुक्रम बिंदु नहीं है । खाने / पीने के उदाहरण में, है के बीच अनुक्रम बिंदु के रूप में और । k++kii++
फिलिप पॉटर

3
@Pipip: इसका कोई मतलब नहीं है। फन () और सन () के बीच एक अनुक्रम बिंदु मौजूद है, लेकिन उनके तर्क के बीच अनुक्रम बिंदु मौजूद नहीं हैं। यह कहना पसंद है, के बीच eat()और sleep()अनुक्रम बिंदु मौजूद है, लेकिन वहाँ भी तर्क एक नहीं है। अनुक्रम बिंदुओं द्वारा अलग किए गए दो फ़ंक्शन कॉल के तर्क एक ही अनुक्रम बिंदुओं से कैसे संबंधित हो सकते हैं ?
नवाज
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.