हटाए गए पॉइंटर के साथ विज़ुअल स्टूडियो क्या करता है और क्यों?


130

एक सी ++ पुस्तक मैं पढ़ रहा हूं कि जब कोई सूचक deleteऑपरेटर को उस स्थान पर मेमोरी का उपयोग करके हटा दिया जाता है जो यह इंगित करता है कि "मुक्त" है और इसे अधिलेखित किया जा सकता है। इसमें यह भी कहा गया है कि जब तक पुन: असाइन या सेट नहीं किया जाता है, तब तक पॉइंटर उसी स्थान को इंगित करता रहेगा NULL

विज़ुअल स्टूडियो 2012 में हालांकि; यह मामला प्रतीत नहीं होता है!

उदाहरण:

#include <iostream>

using namespace std;

int main()
{
    int* ptr = new int;
    cout << "ptr = " << ptr << endl;
    delete ptr;
    cout << "ptr = " << ptr << endl;

    system("pause");

    return 0;
}

जब मैं इस कार्यक्रम को संकलित करता हूं और चलाता हूं तो मुझे निम्नलिखित आउटपुट मिलते हैं:

ptr = 0050BC10
ptr = 00008123
Press any key to continue....

स्पष्ट रूप से वह पता जिसे सूचक डिलीट होने पर बदलावों की ओर इशारा करता है!

ये क्यों हो रहा है? यह विशेष रूप से Visual Studio के साथ कुछ करने के लिए है?

और यदि डिलीट किए गए पते को वैसे भी बदल सकते हैं, तो वह NULLकुछ यादृच्छिक पते के बजाय सूचक को स्वचालित रूप से सेट क्यों नहीं करेगा ?


4
एक पॉइंटर हटाएं, इसका मतलब यह नहीं है कि इसे NULL पर सेट किया जाएगा, आपको इस बात का ध्यान रखना होगा।
मैट

11
मुझे पता है कि, लेकिन मैं जिस किताब को पढ़ रहा हूं, वह विशेष रूप से कहती है कि इसमें अभी भी वही पता होगा जो इसे हटाने से पहले इंगित कर रहा था, लेकिन उस पते की सामग्री को अधिलेखित किया जा सकता है।
tjwrona1992

6
@ tjwrona1992, हाँ, क्योंकि यह वही है जो आमतौर पर हो रहा है। पुस्तक केवल सबसे संभावित परिणाम को सूचीबद्ध करती है, न कि कठोर नियम को।
सर्गेई

5
@ tjwrona1992 A C ++ पुस्तक मैं पढ़ रहा हूँ - और पुस्तक का नाम है ...?
पॉलमेकेंजी

4
@ tjwrona1992: यह आश्चर्य की बात हो सकती है, लेकिन यह अवैध सूचक मूल्य का सभी उपयोग है जो अपरिभाषित व्यवहार है, न कि केवल डेरेफेरिंग। "जाँच कर रहा है कि यह कहाँ इंगित कर रहा है" एक अस्वीकृत तरीके से मूल्य का उपयोग कर रहा है।
बेन वोइगट

जवाबों:


175

मैंने देखा कि जो पता संग्रहीत किया ptrगया था वह हमेशा के साथ अधिलेखित किया जा रहा था 00008123...

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

... NULL के लिए चेक एक सामान्य कोड निर्माण है जिसका अर्थ है कि NULL के लिए एक मौजूदा चेक NULL के साथ संयुक्त रूप से एक संकरण मूल्य के रूप में संयुक्त रूप से एक वास्तविक मेमोरी सुरक्षा मुद्दे को छिपा सकता है जिसका मूल कारण वास्तव में पते की आवश्यकता है।

इस कारण से हमने 0x8123 को एक स्वच्छता मूल्य के रूप में चुना है - एक ऑपरेटिंग सिस्टम के नजरिए से यह शून्य पते (NULL) के रूप में एक ही मेमोरी पेज में है, लेकिन 0x8123 पर एक पहुंच उल्लंघन बेहतर रूप से डेवलपर को अधिक विस्तृत ध्यान देने की आवश्यकता है। ।

न केवल यह बताता है कि विज़ुअल स्टूडियो पॉइंटर को हटाने के बाद क्या करता है, यह भी जवाब देता है कि उन्होंने इसे NULLस्वचालित रूप से सेट करने के लिए क्यों नहीं चुना !


यह "फ़ीचर" "एसडीएल चेक" सेटिंग के हिस्से के रूप में सक्षम है। इसे सक्षम / अक्षम करने के लिए: PROJECT -> गुण -> कॉन्फ़िगरेशन गुण -> C / C ++ -> सामान्य -> ​​SDL जाँच

इसकी पुष्टि करने के लिए:

इस सेटिंग को बदलना और समान कोड को फिर से लिखना निम्न आउटपुट उत्पन्न करता है:

ptr = 007CBC10
ptr = 007CBC10

"सुविधा" उद्धरण में है क्योंकि ऐसे मामले में जहां आपके पास एक ही स्थान पर दो पॉइंटर्स हैं, डिलीट कॉलिंग उनमें से केवल एक को सैनिटाइज़ करेगा । अन्य को अमान्य स्थान की ओर इंगित किया जाएगा ...


अपडेट करें:

C ++ प्रोग्रामिंग अनुभव के 5 और वर्षों के बाद मुझे एहसास हुआ कि यह पूरा मुद्दा मूल रूप से एक लूट बिंदु है। आप एक सी ++ प्रोग्रामर हैं और अभी भी उपयोग कर रहे हैं newऔर deleteबजाय स्मार्ट संकेत (जो इस पूरे मुद्दे को दरकिनार) आप कैरियर मार्ग में बदलाव करने पर विचार करने के लिए एक सी प्रोग्रामर बनने के लिए चाहते हो सकता है का उपयोग कर के कच्चे संकेत प्रबंधन करने के लिए। ;)


12
यह एक अच्छी खोज है। काश एमएस इस तरह से डिबगिंग व्यवहार को बेहतर दस्तावेज़ बनाता। उदाहरण के लिए, यह जानना अच्छा होगा कि किस संकलक संस्करण ने इसे लागू करना शुरू किया और कौन से विकल्प व्यवहार को सक्षम / अक्षम करते हैं।
माइकल बूर

5
"एक ऑपरेटिंग सिस्टम के नजरिए से यह शून्य पते के समान मेमोरी पेज में है" - हुह? क्या Windows और linux दोनों के लिए x86 पर मानक (बड़े पृष्ठों की अनदेखी) पृष्ठ का आकार अभी भी 4kb नहीं है? हालांकि मुझे रेमंड चेन के ब्लॉग पर पहले 64kb पते की जगह के बारे में कुछ याद है, इसलिए व्यवहार में मैं इसे समान परिणाम देता हूं,
Voo

12
@Voo फँसाने के लिए मृत स्थान के रूप में रैम के पहले (और अंतिम) 64kB मूल्य का भंडार है। 0x8123 अच्छी तरह से वहाँ गिरता है
शाफ़्ट सनकी

7
वास्तव में, यह बुरी आदतों को प्रोत्साहित नहीं करता है, और यह आपको सूचक को नल को सेट करने की अनुमति नहीं देता है - यही कारण है कि वे 0x8123इसके बजाय उपयोग कर रहे हैं 0। पॉइंटर अभी भी अमान्य है, लेकिन इसे (अच्छा) मानने का प्रयास करते समय एक अपवाद का कारण बनता है, और यह NULL चेक को पास नहीं करता है (यह भी अच्छा है, क्योंकि यह ऐसा नहीं करने के लिए एक त्रुटि है)। बुरी आदतों के लिए जगह कहाँ है? यह वास्तव में सिर्फ कुछ है जो आपको डिबग करने में मदद करता है।
लुआॅन

3
खैर, यह दोनों (सभी) को सेट नहीं कर सकता है, इसलिए यह दूसरा सबसे अच्छा विकल्प है। यदि आप इसे पसंद नहीं करते हैं, तो बस एसडीएल चेक बंद करें - मैं उन्हें उपयोगी नहीं पाता, खासकर जब किसी और के कोड को डीबग करना।
लुआं

30

आप के दुष्प्रभाव देखते हैं /sdl संकलन विकल्प के । VS2015 परियोजनाओं के लिए डिफ़ॉल्ट रूप से चालू, यह अतिरिक्त सुरक्षा जांच को सक्षम करता है जो कि / gs द्वारा प्रदान की जाती है। प्रोजेक्ट> गुण> C / C ++> General> SDL चेक सेटिंग को बदलने के लिए उपयोग करें।

MSDN लेख से उद्धरण :

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

ध्यान रखें कि जब आप MSVC का उपयोग करते हैं तो NULL को हटाए गए पॉइंटर्स सेट करना एक बुरा अभ्यास है। यह आपको डीबग हीप और इस / sdl ऑप्शन दोनों से मिलने वाली मदद को पराजित करता है, आप अपने प्रोग्राम में अवैध फ्री / डिलीट कॉल का पता नहीं लगा सकते हैं।


1
की पुष्टि। इस सुविधा को अक्षम करने के बाद, सूचक अब पुनर्निर्देशित नहीं किया गया है। वास्तविक सेटिंग प्रदान करने के लिए धन्यवाद जो इसे संशोधित करता है!
tjwrona1992

हंस, क्या अभी भी हटाए गए पॉइंटर्स को NULL के मामले में सेट करने के लिए बुरा व्यवहार माना जाता है, जहां आपके पास दो पॉइंटर्स एक ही स्थान पर इंगित होते हैं? जब आप deleteएक, विजुअल स्टूडियो अपने मूल स्थान की ओर इशारा करते हुए दूसरा पॉइंटर छोड़ेंगे जो अब अमान्य है।
tjwrona1992

1
मेरे लिए बहुत स्पष्ट नहीं है कि आप किस तरह के जादू को सूचक को NULL को सेट करने से होने की उम्मीद करते हैं। अन्य पॉइंटर ऐसा नहीं है जिससे यह कुछ भी हल नहीं करता है, फिर भी आपको बग ढूंढने के लिए डीबग आवंटनकर्ता की आवश्यकता है।
हंस पसंत

3
वीएस पॉइंटर्स को साफ नहीं करता है । यह उन्हें भ्रष्ट करता है। जब भी आप उनका उपयोग करेंगे तो आपका प्रोग्राम क्रैश हो जाएगा। डिबग एलोकेटर हीप मेमोरी के साथ बहुत कुछ करता है। NULL की बड़ी समस्या, यह पर्याप्त भ्रष्ट नहीं है। अन्यथा एक आम रणनीति, Google "0xdeadbeef"।
हंस पैसेंट

1
NULL को पॉइंटर सेट करना अभी भी अपने पिछले पते की ओर इशारा करते हुए छोड़ने से बहुत बेहतर है जो अब अमान्य है। NULL पॉइंटर पर लिखने का प्रयास किसी भी डेटा को दूषित नहीं करेगा और संभवतः प्रोग्राम को क्रैश कर देगा। उस बिंदु पर सूचक का पुन: उपयोग करने का प्रयास भी कार्यक्रम को दुर्घटनाग्रस्त नहीं कर सकता है, यह सिर्फ बहुत अप्रत्याशित परिणाम पैदा कर सकता है!
tjwrona1992

19

इसमें यह भी कहा गया है कि पॉइंटर को उसी स्थान पर इंगित करना जारी रखा जाएगा जब तक कि इसे पुन: असाइन नहीं किया जाता है या NULL को सेट नहीं किया जाता है।

यह निश्चित रूप से भ्रामक जानकारी है।

स्पष्ट रूप से वह पता जिसे सूचक डिलीट होने पर बदलावों की ओर इशारा करता है!

ये क्यों हो रहा है? यह विशेष रूप से Visual Studio के साथ कुछ करने के लिए है?

यह स्पष्ट रूप से भाषा विनिर्देशों के भीतर है। ptrकॉल के बाद मान्य नहीं है delete। इसका उपयोग करने के ptrबाद deleted का उपयोग अपरिभाषित व्यवहार के कारण होता है। यह मत करो। रन टाइम पर्यावरण ptrकॉल करने के बाद इसके साथ जो करना चाहता है करने के लिए स्वतंत्र है delete

और यदि डिलीट किया गया पता बदल सकता है तो यह वैसे भी इंगित कर रहा है, क्यों कुछ स्वचालित रैंडम के बजाय NULL के लिए पॉइंटर को स्वचालित रूप से सेट नहीं करेगा ???

सूचक के मूल्य को किसी भी पुराने मूल्य में बदलना भाषा विनिर्देश के भीतर है। जहां तक ​​इसे NULL में बदलने की बात है, मैं कहूंगा कि यह बुरा होगा। यदि सूचक NULL के लिए सेट किया गया था, तो प्रोग्राम अधिक सेंस तरीके से व्यवहार करेगा। हालाँकि, यह समस्या को छिपाएगा। जब कार्यक्रम को अलग-अलग अनुकूलन सेटिंग्स के साथ संकलित किया जाता है या एक अलग वातावरण में पोर्ट किया जाता है, तो समस्या संभवतः सबसे असंगत क्षण में दिखाई देगी।


1
मुझे विश्वास नहीं है कि यह ओपी के सवाल का जवाब देता है।
सर्गेई

संपादित करने के बाद भी असहमत। इसे NULL में सेट करने से समस्या छिपी नहीं रहेगी - वास्तव में, यह इसके बिना अधिक से अधिक मामलों में इसका विस्तार करेगा। एक कारण यह है कि सामान्य कार्यान्वयन ऐसा नहीं करते हैं, और कारण अलग है।
सर्बिया

4
@SergeyA, अधिकांश कार्यान्वयन दक्षता के लिए ऐसा नहीं करते हैं। हालांकि, यदि कोई कार्यान्वयन इसे निर्धारित करने का निर्णय लेता है, तो इसे उस चीज़ के लिए सेट करना बेहतर है जो NULL नहीं है। यदि यह NULL पर सेट किया गया था तो यह जल्द ही समस्याओं को प्रकट करेगा। यह NULL के लिए सेट है, deleteपॉइंटर पर दो बार कॉल करने से कोई समस्या नहीं होगी। यह निश्चित रूप से अच्छा नहीं है।
आर साहू

नहीं, दक्षता नहीं - कम से कम, यह प्राथमिक चिंता नहीं है।
सर्गेई

7
@SergeyA एक सूचक को ऐसे मान पर सेट कर रहा है जो NULLनिश्चित रूप से प्रक्रिया के बाहर भी नहीं है 'पता स्थान दो विकल्पों की तुलना में अधिक मामलों को उजागर करेगा। यदि इसे मुक्त होने के बाद उपयोग किया जाता है, तो इसे झूलना अनिवार्य रूप से एक segfault का कारण नहीं होगा; NULLयदि यह deleteफिर से d है तो इसे सेटिगफ़ॉल्ट सेट करने का कारण नहीं बन सकता ।
ब्लैकलाइट शाइनिंग

10
delete ptr;
cout << "ptr = " << ptr << endl;

सामान्य रूप से पढ़ने में भी (जैसे आप ऊपर करते हैं, ध्यान दें: यह डेरेफेरिंग से अलग है) अमान्य पॉइंटर्स के मान (सूचक उदाहरण के लिए अमान्य हो जाता है जब आप deleteइसे लागू करते हैं) परिभाषित व्यवहार है। यह CWG # 1438 में पेश किया गया था । यह भी देखें यहाँ

कृपया ध्यान दें कि इससे पहले अवैध संकेत के पढ़ने के मान अपरिभाषित व्यवहार थे, इसलिए आपके पास जो कुछ भी है वह अपरिभाषित व्यवहार होगा, जिसका अर्थ है कि कुछ भी हो सकता है।


3
यह भी प्रासंगिक उद्धरण है [basic.stc.dynamic.deallocation]: "यदि मानक पुस्तकालय में एक डिक्लरेशन फ़ंक्शन के लिए दिया गया तर्क एक पॉइंटर है जो शून्य पॉइंटर वैल्यू नहीं है, तो डीलक्लोलेशन फ़ंक्शन पॉइंटर द्वारा संदर्भित स्टोरेज को हटा देगा, जिसमें सभी बिंदुओं का संदर्भ दिया जाएगा। [conv.lval]डीलक्लोकेटेड स्टोरेज का हिस्सा "और नियम (धारा 4.1) जो रीडिंग (lvalue-> rvalue रूपांतरण) कहते हैं, किसी भी अमान्य पॉइंटर वैल्यू का कार्यान्वयन-परिभाषित व्यवहार है।
बेन वोइग्ट

यहां तक ​​कि यूबी को एक विशिष्ट विक्रेता द्वारा एक विशिष्ट तरीके से लागू किया जा सकता है जैसे कि यह विश्वसनीय है, कम से कम उस संकलक के लिए। यदि Microsoft ने CWG # 1438 से पहले अपने पॉइंटर-सैनिटाइजेशन फ़ीचर को लागू करने का फैसला किया था, तो इससे वह सुविधा कम या ज्यादा विश्वसनीय नहीं होगी, और विशेष रूप से यह सच नहीं है कि "कुछ भी हो सकता है" यदि यह सुविधा चालू हो। भले ही मानक क्या कहे।
काइल स्ट्रैंड

@ केलीस्ट्रैंड: मैंने मूल रूप से यूबी की परिभाषा दी है ( blog.regehr.org/archives/213 )।
जियोर्जिअम

1
SO पर C ++ समुदाय के अधिकांश लोगों के लिए, "कुछ भी हो सकता है" पूरी तरह से शाब्दिक रूप से लिया जाता है । मुझे लगता है कि यह हास्यास्पद है । मैं यूबी की परिभाषा को समझता हूं, लेकिन मैं यह भी समझता हूं कि कंपाइलर वास्तविक लोगों द्वारा लागू किए गए सॉफ़्टवेयर के टुकड़े हैं, और यदि वे लोग कंपाइलर को लागू करते हैं ताकि यह कुछ निश्चित तरीके से व्यवहार करे , तो यह है कि कंपाइलर कैसे व्यवहार करेगा , चाहे मानक क्या कहे ।
काइल स्ट्रैंड

1

मेरा मानना ​​है कि, आप किसी प्रकार का डिबग मोड चला रहे हैं और वीएस आपके पॉइंटर को किसी ज्ञात स्थान पर पुन: स्थापित करने का प्रयास कर रहा है, ताकि आगे इसे हटाने की कोशिश की जा सके। रिलीज मोड में एक ही कार्यक्रम को संकलित / चलाने का प्रयास करें।

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

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


हाँ, यहां तक ​​कि निरंतर संकेत इस रहस्यमय 8123 पर पुनर्निर्देशित हो जाते हैं!
tjwrona1992

वीएस के पास एक और पत्थर जाता है :) बस आज सुबह किसी ने पूछा कि उन्हें वीएस के बजाय जी ++ का उपयोग क्यों करना चाहिए। ये रहा।
सर्गेई

7
@SergeyA लेकिन दूसरी तरफ से डेरेफ़िंग जो डिलीट किए गए पॉइंटर से सेगफ़ॉल्ट से आपको दिखेगी कि आपने डिलीट किए गए पॉइंटर को डीरफ़ करने की कोशिश की थी और यह NULL के बराबर नहीं होगा। अन्य मामले में यह केवल दुर्घटनाग्रस्त होगा यदि पृष्ठ भी मुक्त हो जाता है (जो बहुत संभावना नहीं है)। तेजी से विफल; जल्दी ही हल करो।
शाफ़्ट सनकी

1
@ratchetfreak "फेल फास्ट, सॉल्विंग सोलर" एक बहुत ही मूल्यवान मंत्र है, लेकिन "महत्वपूर्ण फोरेंसिक सबूतों को नष्ट करने में तेजी से विफल" ऐसा मूल्यवान मंत्र शुरू नहीं करता है। साधारण मामलों में, यह सुविधाजनक हो सकता है, लेकिन अधिक जटिल मामलों में (जिन पर हमें सबसे अधिक सहायता की आवश्यकता होती है), बहुमूल्य जानकारी को मिटाने से समस्या को हल करने के लिए उपलब्ध मेरे उपकरण कम हो जाते हैं।
कॉर्ट अमोन

2
@ tjwrona1992: Microsoft मेरी राय में यहाँ सही काम कर रहा है। एक पॉइंटर को साफ करना किसी को भी करने से बेहतर है। और अगर इससे आपको डीबगिंग में समस्या होती है, तो खराब डिलीट कॉल से पहले एक ब्रेक प्वाइंट लगाएं। अजीब बात है कि इस तरह से कुछ के बिना आप समस्या कभी नहीं होगा। और अगर आपके पास इन बगों का पता लगाने के लिए एक बेहतर समाधान है, तो इसका उपयोग करें और आपको परवाह क्यों है कि Microsoft क्या करता है?
ज़ैन लिंक्स

0

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

एक और बिंदु यह है कि कुछ रन टाइम ऑप्टिमाइज़र उस मूल्य की जांच कर सकते हैं और उसके परिणाम बदल सकते हैं।

पहले के समय में MS मूल्य निर्धारित करता था 0xcfffffff

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.