अशक्त उदाहरण पर एक सदस्य फ़ंक्शन को लागू करने के लिए अपरिभाषित व्यवहार कब होता है?


120

निम्नलिखित कोड पर विचार करें:

#include <iostream>

struct foo
{
    // (a):
    void bar() { std::cout << "gman was here" << std::endl; }

    // (b):
    void baz() { x = 5; }

    int x;
};

int main()
{
    foo* f = 0;

    f->bar(); // (a)
    f->baz(); // (b)
}

हम (b)दुर्घटना की उम्मीद करते हैं, क्योंकि xअशक्त सूचक के लिए कोई संगत सदस्य नहीं है । व्यवहार में, (a)क्रैश नहीं होता है क्योंकि thisपॉइंटर का उपयोग कभी नहीं किया जाता है।

क्योंकि (b)dereferences thisसूचक ( (*this).x = 5;), और thisरिक्त है, कार्यक्रम, अपरिभाषित व्यवहार में प्रवेश करती है के रूप में अपसंदर्भन अशक्त हमेशा कहा जाता है कि अपरिभाषित व्यवहार किया जाना है।

क्या (a)अपरिभाषित व्यवहार में परिणाम होता है? क्या होगा अगर दोनों कार्य (और x) स्थिर हैं?


यदि दोनों कार्य स्थिर हैं , तो x को baz के अंदर कैसे संदर्भित किया जा सकता है ? (x एक गैर-स्थैतिक सदस्य चर है)
legends2k

4
@ किंवदंतियां 2k: प्रिटेंड xको भी स्थिर बनाया गया था। :)
GManNickG

निश्चित रूप से, लेकिन मामले (ए) के लिए यह सभी मामलों में एक ही काम करता है, अर्थात, फ़ंक्शन को लागू किया जाता है। हालाँकि, सूचक के मान को 0 से 1 तक बदलना (कहते हैं, reinterpret_cast के माध्यम से), यह लगभग हमेशा दुर्घटनाग्रस्त होता है। क्या 0 का मान आबंटन और इस प्रकार NULL, जैसा कि मामले में, कंपाइलर के लिए कुछ विशेष का प्रतिनिधित्व करता है? हमेशा इसे आवंटित किसी अन्य मूल्य के साथ क्रैश क्यों होता है?
सिद्धार्थ शंकरन

5
दिलचस्प: सी ++ का अगला संशोधन आओ, संकेत देने वालों की कोई और डीफ्रेंसिंग नहीं होगी। अब हम संकेत के माध्यम से अप्रत्यक्ष प्रदर्शन करेंगे । अधिक जानने के लिए, कृपया इस लिंक के माध्यम से अप्रत्यक्ष प्रदर्शन करें: N3362
जेम्स

3
एक शून्य सूचक पर एक सदस्य फ़ंक्शन को आमंत्रित करना हमेशा अपरिभाषित व्यवहार होता है। बस आपके कोड को देखकर, मैं पहले से ही अपरिभाषित व्यवहार को महसूस कर सकता हूं, धीरे-धीरे मेरी गर्दन पर रेंगना!
फ्रेडओवरफ्लो

जवाबों:


113

दोनों (a)और (b)अपरिभाषित व्यवहार में परिणाम। अशक्त सूचक के माध्यम से सदस्य फ़ंक्शन को कॉल करने के लिए हमेशा अपरिभाषित व्यवहार होता है। यदि फ़ंक्शन स्थिर है, तो यह तकनीकी रूप से भी अपरिभाषित है, लेकिन कुछ विवाद है।


समझने वाली पहली बात यह है कि अशक्त सूचक को निष्क्रिय करने के लिए अपरिभाषित व्यवहार क्यों है। C ++ 03 में, वास्तव में यहाँ थोड़ी अस्पष्टता है।

यद्यपि "अपरिभाषित व्यवहार में अशक्त सूचक परिणाम" का उल्लेख encing1.9 / 4 और /8.3.2 / 4 दोनों में नोटों में किया गया है, यह स्पष्ट रूप से कभी नहीं कहा गया है। (नोट गैर-मानक हैं।)

हालाँकि, कोई इसे /3.10 / 2 से घटाने का प्रयास कर सकता है:

एक अंतराल एक वस्तु या कार्य को संदर्भित करता है।

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

भले ही, यह निश्चित रूप से एक प्रतिद्वंद्विता में परिवर्तित नहीं किया जा सकता (/4.1 / 1):

यदि जिस वस्तु से लैवल्यू संदर्भित होता है, वह प्रकार T की वस्तु नहीं है और T से प्राप्त प्रकार की वस्तु नहीं है, या यदि वस्तु असिंचित है, तो इस रूपांतरण की आवश्यकता वाले प्रोग्राम में अपरिभाषित व्यवहार होता है।

यहाँ यह निश्चित रूप से अपरिभाषित व्यवहार है।

अस्पष्टता यह है कि यह अपरिभाषित व्यवहार करने के लिए है या नहीं, लेकिन यह अमान्य सूचक से मान का उपयोग नहीं है (अर्थात, एक अंतराल प्राप्त करें, लेकिन इसे एक प्रतिद्वंद्विता में परिवर्तित न करें)। यदि नहीं, तो int *i = 0; *i; &(*i);अच्छी तरह से परिभाषित है। यह एक सक्रिय मुद्दा है

तो हमारे पास एक सख्त "डेरेक्शन ऑफ़ ए नल पॉइंटर है, अपरिभाषित व्यवहार प्राप्त करें" दृश्य और एक कमजोर "एक डिरेल्ड नेल पॉइंटर का उपयोग करें, अपरिभाषित व्यवहार प्राप्त करें" दृश्य।

अब हम इस प्रश्न पर विचार करते हैं।


हां, (a)अपरिभाषित व्यवहार का परिणाम है। वास्तव में, यदि thisशून्य है तो फ़ंक्शन की सामग्री की परवाह किए बिना परिणाम अपरिभाषित है।

यह follows5.2.5 / 3 से इस प्रकार है:

यदि E1टाइप "पॉइंटर टू क्लास एक्स" है, तो अभिव्यक्ति E1->E2को समकक्ष रूप में बदल दिया जाता है(*(E1)).E2;

*(E1)एक सख्त व्याख्या के साथ अपरिभाषित व्यवहार का परिणाम देगा, और .E2इसे एक प्रतिद्वंद्विता में परिवर्तित करता है, जिससे यह कमजोर व्याख्या के लिए अपरिभाषित व्यवहार करता है।

यह इस प्रकार भी है कि यह सीधे से अपरिभाषित व्यवहार है (.19.3.1 / 1):

यदि किसी वर्ग X के गैर-स्थैतिक सदस्य फ़ंक्शन को किसी ऑब्जेक्ट के लिए कहा जाता है जो कि X का नहीं है, या X से प्राप्त एक प्रकार का है, तो व्यवहार अपरिभाषित है।


स्थिर कार्यों के साथ, सख्त बनाम कमजोर व्याख्या अंतर बनाती है। कड़ाई से बोलते हुए, यह अपरिभाषित है:

क्लास सदस्य एक्सेस सिंटैक्स का उपयोग करके एक स्थिर सदस्य को संदर्भित किया जा सकता है, जिस स्थिति में ऑब्जेक्ट-एक्सप्रेशन का मूल्यांकन किया जाता है।

यही है, यह मूल्यांकन किया जाता है जैसे कि यह गैर-स्थैतिक थे और हम एक बार फिर से एक अशक्त सूचक को निष्क्रिय कर देते हैं (*(E1)).E2

हालाँकि, क्योंकि E1स्थैतिक सदस्य-फ़ंक्शन कॉल में उपयोग नहीं किया जाता है, यदि हम कमजोर व्याख्या का उपयोग करते हैं तो कॉल अच्छी तरह से परिभाषित है। *(E1)एक परिणाम में, स्थैतिक कार्य हल हो जाता है, *(E1)त्याग दिया जाता है, और फ़ंक्शन को कहा जाता है। वहाँ कोई अंतराल-से-अंतराल परिवर्तन नहीं है, इसलिए कोई अपरिभाषित व्यवहार नहीं है।

C ++ 0x में, n3126 के रूप में, अस्पष्टता बनी हुई है। अभी के लिए, सुरक्षित रहें: सख्त व्याख्या का उपयोग करें।


5
+1। "कमजोर परिभाषा" के तहत पांडेंट्री को जारी रखना नॉनस्टैटिक मेंबर फंक्शन को "उस ऑब्जेक्ट के लिए नहीं कहा जाता है जो टाइप टाइप का नहीं है"। इसे एक ऐसे अंतराल के लिए कहा जाता है जो एक वस्तु नहीं है। इसलिए प्रस्तावित समाधान पाठ को जोड़ता है "या यदि आपके द्वारा दिए गए खंड के लिए लैवल्यू एक खाली अंतराल है"।
स्टीव जेसप

क्या आप थोड़ा स्पष्ट कर सकते हैं? विशेष रूप से, आपके "बंद मुद्दे" और "सक्रिय मुद्दे" लिंक के साथ, अंक संख्याएं क्या हैं? इसके अलावा, अगर यह एक बंद मुद्दा है, तो वास्तव में हां / स्थिर कार्यों के लिए कोई जवाब नहीं है? मुझे ऐसा लगता है कि मैं आपके उत्तर को समझने की कोशिश में अंतिम चरण को याद कर रहा हूं।
ब्रूक्स मूसा

4
मुझे नहीं लगता कि सीडब्ल्यूजी दोष 315 "बंद" है जैसा कि "बंद मुद्दों" पृष्ठ पर इसकी उपस्थिति का अर्थ है। औचित्य कहता है कि इसे अनुमति दी जानी चाहिए क्योंकि " *pजब pशून्य है तब तक कोई त्रुटि नहीं है जब तक कि अंतराल को एक प्रतिद्वंद्विता में परिवर्तित नहीं किया जाता है।" हालांकि, यह एक "खाली अंतराल" की अवधारणा पर निर्भर करता है, जो कि CWG दोष 232 के प्रस्तावित प्रस्ताव का हिस्सा है , लेकिन जिसे अपनाया नहीं गया है। इसलिए, C ++ 03 और C ++ 0x, दोनों में भाषा के साथ, नल सूचक को निष्क्रिय करना अभी भी अपरिभाषित है, भले ही कोई अंतराल-से-प्रतिद्वंद्विता रूपांतरण न हो।
जेम्स मैकनेलिस

1
@JamesMcNellis: मेरी समझ से, यदि pएक हार्डवेयर पता था जो पढ़ने पर कुछ कार्रवाई को ट्रिगर करेगा, लेकिन घोषित नहीं किया गया था volatile, तो कथन की *p;आवश्यकता नहीं होगी, लेकिन वास्तव में उस पते को पढ़ने की अनुमति होगी ; &(*p);हालाँकि, कथन को ऐसा करने से मना किया जाएगा। यदि *pथे volatile, तो पढ़ने की आवश्यकता होगी। या तो मामले में, यदि सूचक अमान्य है, तो मैं यह नहीं देख सकता कि पहला बयान कैसे अपरिभाषित व्यवहार नहीं होगा, लेकिन मैं यह भी नहीं देख सकता कि दूसरा बयान क्यों होगा।
supercat

1
".E2 इसे एक प्रतिद्वंद्विता में परिवर्तित करता है," - उह, नहीं, यह नहीं है
एमएम

30

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

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

Microsoft का MFC फ़ंक्शन GetSafeHwnd वास्तव में इस व्यवहार पर निर्भर करता है। मुझे नहीं पता कि वे क्या धूम्रपान कर रहे थे।

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


1
GetSafeHwnd पहले करता है! यह जाँच करता है और यदि सही है, तो NULL देता है। फिर यह एक SEH फ्रेम शुरू करता है और पॉइंटर को पॉइंटर करता है। यदि मेमोरी एक्सेस उल्लंघन है (0xc0000005) यह पकड़ा गया है और NULL को कॉलर पर लौटाया गया है :) Else the HWND वापस आ गया है।
Пет14р Петров

@ Пет aрПетров जब से मैं इस कोड को देख रहा हूं, तब से काफी साल हो गए हैं GetSafeHwnd, यह संभव है कि उन्होंने तब से इसे बढ़ाया है। और यह मत भूलो कि उन्हें संकलक के कामकाज पर ज्ञान है!
मार्क रैनसम

मैं एक नमूना संभव कार्यान्वयन को बता रहा हूं जिसका एक ही प्रभाव है, यह वास्तव में क्या करता है एक डिबगर का उपयोग करके रिवर्स-इंजीनियर किया जा सकता है :)
Петър Петров

1
"उन्हें संकलक के कामकाज पर अंदरूनी जानकारी है!" - MinGW जैसी परियोजनाओं के लिए शाश्वत परेशानी का कारण है कि जी + + को कोड को संकलित करने का प्रयास जो कि विंडोज एपीआई को कॉल करता है
MM

@MM मुझे लगता है कि हम सभी सहमत होंगे कि यह अनुचित है। और उस वजह से, मुझे भी लगता है कि संगतता के बारे में एक कानून है जो इसे रखने के लिए इसे थोड़ा सा अवैध बनाता है।
v.oddou
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.