Cout का सही उत्तर क्या है << a ++ <<??


98

हाल ही में एक साक्षात्कार में निम्नलिखित वस्तुनिष्ठ प्रकार का प्रश्न था।

int a = 0;
cout << a++ << a;

उत्तर:

ए। 10
बी। 01
स। अपरिभाषित व्यवहार

मैंने जवाब दिया बी, यानी आउटपुट "01" होगा।

लेकिन बाद में मेरे आश्चर्य से मुझे एक साक्षात्कारकर्ता ने बताया कि सही उत्तर विकल्प c: अपरिभाषित है।

अब, मैं सी ++ में अनुक्रम बिंदुओं की अवधारणा को जानता हूं। व्यवहार निम्न कथन के लिए अपरिभाषित है:

int i = 0;
i += i++ + i++;

लेकिन मेरे बयान के लिए समझने के अनुसार cout << a++ << a, ostream.operator<<()दो बार पहले से ही कहा जाता है, ostream.operator<<(a++)और बाद में ostream.operator<<(a)

मैंने VS2010 संकलक पर परिणाम की जाँच की और इसका आउटपुट भी '01' है।


30
क्या आपने स्पष्टीकरण मांगा? मैं अक्सर संभावित उम्मीदवारों का साक्षात्कार लेता हूं और प्रश्न प्राप्त करने में काफी दिलचस्पी रखता हूं, यह रुचि दिखाता है।
ब्रैडी

3
@ जर्क यह अपरिभाषित व्यवहार है। कार्यान्वयन कुछ भी करता है (अपने बॉस के नाम में अपमानजनक ईमेल भेजने सहित) अनुरूप है।
जेम्स कांजे

2
यह प्रश्न C ++ 11 (C ++ का वर्तमान संस्करण) उत्तर के लिए रो रहा है जो अनुक्रम बिंदुओं का उल्लेख नहीं करता है। दुर्भाग्य से मैं सी ++ 11 में अनुक्रम बिंदुओं के प्रतिस्थापन के बारे में पर्याप्त जानकार नहीं हूं।
सीबी बेली

3
यदि यह अपरिभाषित नहीं था 10, तो यह निश्चित रूप से नहीं हो सकता है , यह 01या तो होगा 00। ( c++हमेशा मूल्य के लिए मूल्यांकन करेंगे cपड़ा से पहले वृद्धि की जाती है)। और अगर यह अपरिभाषित नहीं था, तब भी यह भ्रामक होगा।
लेफ्टअरबाउट

2
हां पता है, जब मैंने "cout << c ++ << c" शीर्षक पढ़ा, तो मैंने पल-पल इसे C और C ++ भाषाओं के बीच संबंध के बारे में एक बयान के रूप में सोचा, और कुछ अन्य ने "cout" नाम दिया। तुम्हें पता है, जैसे किसी कह रहे थे कि वे कैसे सोचा कि "अदालत" अवर ज्यादा करने के लिए सी ++, और कहा कि सी ++ सी से हीन ज्यादा था - और शायद संक्रामिता द्वारा कि "अदालत" बहुत, था बहुत ज्यादा सी :) अवर
tchrist

जवाबों:


145

आप इसके बारे में सोच सकते हैं:

cout << a++ << a;

जैसा:

std::operator<<(std::operator<<(std::cout, a++), a);

सी ++ गारंटी देता है कि पिछले मूल्यांकन के सभी दुष्प्रभाव अनुक्रम बिंदुओं पर किए गए होंगे । फ़ंक्शन तर्कों के मूल्यांकन के बीच कोई अनुक्रम बिंदु नहीं हैं, जिसका अर्थ है कि तर्क aका मूल्यांकन तर्क से पहले std::operator<<(std::cout, a++)या बाद में किया जा सकता है । अतः उपरोक्त का परिणाम अपरिभाषित है।


C ++ 17 अद्यतन

C ++ 17 में नियमों को अद्यतन किया गया है। विशेष रूप से:

शिफ्ट ऑपरेटर एक्सप्रेशन में E1<<E2और E1>>E2, हर वैल्यू कंप्यूटेशन और साइड-इफ़ेक्ट को E1हर वैल्यू कंप्यूटेशन और साइड इफ़ेक्ट से पहले सीक्वेंस किया जाता है E2

जिसका अर्थ है कि परिणाम के लिए कोड की आवश्यकता होती है b, जो आउटपुट करता है 01

देखें मुहावरेदार सी ++ के लिए P0145R3 रिफाइनिंग अभिव्यक्ति मूल्यांकन आदेश अधिक जानकारी के लिए।


@ मोमिन: विस्तार के लिए धन्यवाद। आपके द्वारा निष्कासित किए गए कॉल के साथ यह अपरिभाषित व्यवहार होगा। लेकिन अब, मेरे पास एक और सवाल है (हो सकता है कि एक सीलर हो सकता है, और मुझे कुछ बुनियादी और जोर से याद आ रही है) आपने यह कैसे घटाया कि एसटीडी का वैश्विक संस्करण :: ऑपरेटर << () को शुतुरमुर्ग के बजाय कहा जाएगा :: ऑपरेटर < <() सदस्य संस्करण। डिबगिंग पर मैं ओस्ट्रीम के एक सदस्य संस्करण में उतर रहा हूं :: ऑपरेटर << () वैश्विक संस्करण के बजाय कॉल करता हूं और यही कारण है कि शुरू में मैंने सोचा था कि उत्तर 01 होगा
pravs

@ मोमिन यह नहीं है कि यह एक अलग बनाता है, लेकिन चूंकि cइसके प्रकार हैं int, operator<<यहां सदस्य कार्य हैं।
जेम्स कान्ज़े

2
@pravs: क्या operator<<कोई सदस्य फ़ंक्शन है या कोई फ़्री-स्टैंडिंग फ़ंक्शन अनुक्रम बिंदुओं को प्रभावित नहीं करता है।
मैक्सिम इगोरुशिन

11
C ++ मानक में अब 'अनुक्रम बिंदु' का उपयोग नहीं किया जाता है। यह अभेद्य था और इसे 'संबंध से पहले / अनुक्रम के बाद अनुक्रम' के साथ बदल दिया गया है।
राफेल डोवगार्ड

2
So the result of the above is undefined.आपका स्पष्टीकरण केवल अनिर्दिष्ट के लिए अच्छा है , अपरिभाषित के लिए नहीं । JamesKanze ने बताया कि हालांकि यह उनके जवाब में कितना अधिक अपरिभाषित है
Deduplicator

68

तकनीकी रूप से, यह समग्र रूप से अपरिभाषित व्यवहार है

लेकिन, उत्तर के दो महत्वपूर्ण पहलू हैं।

कोड स्टेटमेंट:

std::cout << a++ << a;

इस रूप में मूल्यांकन किया जाता है:

std::operator<<(std::operator<<(std::cout, a++), a);

मानक किसी फ़ंक्शन के लिए तर्कों के मूल्यांकन के क्रम को परिभाषित नहीं करता है।
तो या तो:

  • std::operator<<(std::cout, a++) पहले मूल्यांकन किया है या
  • aपहले मूल्यांकन किया है या
  • यह कोई भी कार्यान्वयन परिभाषित आदेश हो सकता है।

यह आदेश मानक के अनुसार अनिर्दिष्ट [Ref 1] है

[Ref १] C ++ ०३.२.२ फंक्शन कॉल पैरा ++

तर्कों के मूल्यांकन का क्रम अनिर्दिष्ट है । फ़ंक्शन में प्रवेश करने से पहले तर्क अभिव्यक्ति मूल्यांकन के सभी दुष्प्रभाव। उपसर्ग अभिव्यक्ति और तर्क अभिव्यक्ति सूची के मूल्यांकन का क्रम अनिर्दिष्ट है।

इसके अलावा, किसी फ़ंक्शन के लिए तर्कों के मूल्यांकन के बीच कोई अनुक्रम बिंदु नहीं है, लेकिन सभी तर्कों [Ref 2] के मूल्यांकन के बाद ही एक अनुक्रम बिंदु मौजूद है ।

[Ref 2] C ++ 03 1.9 कार्यक्रम निष्पादन [intro.execution]:
पैरा 17:

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

ध्यान दें कि, यहाँ का मान cबिना किसी हस्तक्षेप के अनुक्रम बिंदु के बिना एक से अधिक बार पहुँचा जा रहा है, इस बारे में मानक कहते हैं:

[रेफरी ३] सी ++ ०३ ५ एक्सप्रेशंस [एक्सप्र]:
पैरा ४:

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

कोड cअनुक्रम बिंदु के बिना एक से अधिक बार संशोधित करता है और संग्रहीत वस्तु के मूल्य को निर्धारित करने के लिए इसे एक्सेस नहीं किया जा रहा है। यह उपर्युक्त खंड का स्पष्ट उल्लंघन है और इसलिए मानक के अनुसार अनिवार्य परिणाम अपरिभाषित व्यवहार है [Ref 3]


1
तकनीकी रूप से, व्यवहार अपरिभाषित है, क्योंकि एक वस्तु का संशोधन है, और एक हस्तक्षेप अनुक्रम बिंदु के बिना इसे कहीं और एक्सेस करना है। अनिर्धारित अनिर्दिष्ट नहीं है ; यह कार्यान्वयन को और भी अधिक छोड़ देता है।
जेम्स कांजे

1
@ आल्स हां। मैंने आपके संपादन नहीं देखे थे (हालांकि मैं जर्क के बयान पर प्रतिक्रिया दे रहा था कि कार्यक्रम कुछ अजीब नहीं कर सकता है --- यह कर सकता है)। जहाँ तक यह जाता है, आपका संपादित संस्करण अच्छा है, लेकिन मेरे दिमाग में, मुख्य शब्द आंशिक क्रम है ; अनुक्रम बिंदु केवल एक आंशिक आदेश देते हैं।
जेम्स कांजे

1
@ एक विस्तृत विवरण के लिए धन्यवाद, वास्तव में बहुत मददगार !!
pravs

4
नया C ++ 0x मानक अनिवार्य रूप से एक ही है, लेकिन अलग-अलग वर्गों में और अलग-अलग शब्दों में :) :): (1.9 कार्यक्रम निष्पादन [intro.execution], par 15): "यदि किसी स्केलर ऑब्जेक्ट पर साइड इफेक्ट अनपेक्षित सापेक्ष है एक ही स्केलर ऑब्जेक्ट पर एक और साइड इफेक्ट या एक ही स्केलर ऑब्जेक्ट के मूल्य का उपयोग करते हुए एक मूल्य संगणना, व्यवहार unde de ned है। "
राफेल डोवगार्ड

2
मेरा मानना ​​है कि इस उत्तर में एक बग है। "std :: अदालत << c ++ << सी," "std :: ऑपरेटर << (std :: ऑपरेटर << (std :: cout, c ++), c)" का अनुवाद नहीं कर सकता, क्योंकि std :: ऑपरेटर << (std :: ostream &, int) मौजूद नहीं है। इसके बजाय, यह "std :: cout.operator << (c ++)। ऑपरेटर (c)?" का अनुवाद करता है, जिसका वास्तव में "c ++" और "c" के मूल्यांकन के बीच एक अनुक्रम बिंदु होता है (एक अतिभारित ऑपरेटर को माना जाता है फ़ंक्शन कॉल और इसलिए जब फ़ंक्शन कॉल रिटर्न होता है तो एक अनुक्रम बिंदु होता है)। नतीजतन व्यवहार और निष्पादन आदेश है निर्दिष्ट।
क्रिस्टोफर स्मिथ

20

अनुक्रम बिंदु केवल एक आंशिक आदेश को परिभाषित करते हैं । आपके मामले में, आपके पास (एक बार अधिभार संकल्प किया जाता है):

std::cout.operator<<( a++ ).operator<<( a );

a++और पहली कॉल के बीच एक अनुक्रम बिंदु है std::ostream::operator<<, और दूसरी aऔर दूसरी कॉल के बीच एक अनुक्रम बिंदु है std::ostream::operator<<, लेकिन a++और के बीच कोई अनुक्रम बिंदु नहीं है a; केवल ऑर्डर करने की बाधाएँ हैं जिनका a++पहली बार कॉल करने से पहले पूरी तरह से मूल्यांकन (साइड इफेक्ट्स सहित) किया जाता है operator<<, और aदूसरी कॉल करने से पहले दूसरे का पूरी तरह से मूल्यांकन किया जाता है operator<<। (इसमें क्रमिक आदेश बाधाएं भी हैं: दूसरी कॉल operator<<पहले से पहले नहीं हो सकती, क्योंकि इसमें तर्क के रूप में पहले परिणाम की आवश्यकता होती है।) /5 / 4 (C ++ 03) में कहा गया है:

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

अपने अभिव्यक्ति की स्वीकार्य orderings में से एक है a++, a, के लिए पहली कॉल operator<<करने के लिए, दूसरी कॉल operator<<; यह संग्रहीत मान a( a++) को संशोधित करता है , और नए मूल्य (दूसरा a) को निर्धारित करने के अलावा इसे एक्सेस करता है , व्यवहार अनिर्धारित है।


मानक के अपने उद्धरण से एक पकड़। IIRC "को छोड़कर, जहाँ उल्लेख किया गया है", एक ओवरलोड ऑपरेटर के साथ काम करते समय एक अपवाद शामिल है, जो ऑपरेटर को एक फ़ंक्शन के रूप में मानता है और इसलिए std :: ostream :: ऑपरेटर << (int )। अगर मैं गलत हूं कृपया मुझे सही।
क्रिस्टोफर स्मिथ

@ChristopherSmith एक अतिभारित ऑपरेटर एक फ़ंक्शन कॉल की तरह व्यवहार करता है। यदि cएक उपयोगकर्ता प्रकार एक उपयोगकर्ता के साथ परिभाषित किया गया था ++, इसके बजाय int, परिणाम अनिर्दिष्ट होंगे, लेकिन कोई अपरिभाषित व्यवहार नहीं होगा।
जेम्स कंज

1
@ChristopherSmith आप दोनों के बीच एक दृश्य बिंदु कहां दिखाई देते हैं cमें foo(foo(bar(c)), c)? जब कार्यों को बुलाया जाता है, और जब वे लौटते हैं तो एक अनुक्रम बिंदु होता है, लेकिन दोनों के मूल्यांकन के बीच कोई फ़ंक्शन कॉल आवश्यक नहीं है c
जेम्स कान्ज़

1
@ChristopherSmith तो cएक UDT, अतिभारित ऑपरेटरों था होगा फ़ंक्शन कॉल हो, और एक दृश्य बिंदु को पेश होगा, इसलिए व्यवहार अपरिभाषित नहीं किया जाएगा। लेकिन यह अभी भी अनिर्दिष्ट होगा कि क्या उप-अभिव्यक्ति cका मूल्यांकन पहले या बाद में किया गया था c++, इसलिए क्या आपको मिला हुआ संस्करण मिला है या नहीं निर्दिष्ट नहीं किया जाएगा (और सिद्धांत रूप में, प्रत्येक बार समान नहीं होगा)।
जेम्स कान्जे

1
@ChristopherSmith अनुक्रम बिंदु से पहले सब कुछ अनुक्रम बिंदु के बाद कुछ भी होने से पहले होगा। लेकिन अनुक्रम बिंदु केवल एक आंशिक आदेश को परिभाषित करते हैं। प्रश्न में अभिव्यक्ति में, उदाहरण के लिए, उप-अभिव्यक्तियों के बीच कोई अनुक्रम बिंदु नहीं है cऔर c++इसलिए, दोनों किसी भी क्रम में हो सकते हैं। अर्धविराम के लिए ... वे केवल एक अनुक्रम बिंदु पैदा करते हैं जहां तक ​​वे पूर्ण अभिव्यक्ति हैं। अन्य महत्वपूर्ण अनुक्रम अंक समारोह कॉल कर रहे हैं: f(c++)वृद्धि देखेंगे cमें f, और अल्पविराम ऑपरेटर, &&, ||और ?:भी अनुक्रम अंक कारण।
जेम्स कांजे

4

प्रश्न का सही उत्तर प्रश्न देना है। बयान अस्वीकार्य है क्योंकि एक पाठक स्पष्ट जवाब नहीं देख सकता है। इसे देखने का एक और तरीका यह है कि हमने साइड-इफेक्ट्स (सी ++) पेश किए हैं जो बयान को व्याख्या के लिए बहुत कठिन बनाते हैं। संक्षिप्त कोड महान है, बशर्ते इसका अर्थ स्पष्ट हो।


4
प्रश्न एक खराब प्रोग्रामिंग अभ्यास (और यहां तक ​​कि अमान्य C ++) को प्रदर्शित कर सकता है। लेकिन एक जवाब माना जाता है जवाब देने के सवाल का संकेत क्या गलत है और यही कारण है कि यह गलत। सवाल पर एक टिप्पणी का जवाब नहीं है, भले ही वे पूरी तरह से मान्य हों। सबसे अच्छा, यह एक टिप्पणी हो सकती है, उत्तर नहीं।
पीपी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.