2011-12-30 31 views
25

शैक्षणिक उद्देश्यों के लिए, मैं विधियों का एक सेट लिख रहा हूं जो सी # में रनटाइम अपवादों को समझने के लिए कारण हैं कि सभी अपवाद क्या हैं और उन्हें क्या कारण है। अभी, मैं उन कार्यक्रमों के साथ झुका रहा हूं जो AccessViolationException का कारण बनते हैं।* (int *) 0 = 0 क्यों पहुंच उल्लंघन का कारण नहीं है?

(मेरे लिए) सबसे स्पष्ट तरीका यह है, एक संरक्षित स्मृति स्थान के लिए लिखने के लिए इस तरह था:

System.Runtime.InteropServices.Marshal.WriteInt32(IntPtr.Zero, 0); 

बस के रूप में मैं आशा व्यक्त की थी, यह एक AccessViolationException फेंक दिया। मैं इसे अधिक संक्षेप में करना चाहता था, इसलिए मैंने असुरक्षित कोड के साथ एक प्रोग्राम लिखने का फैसला किया, और शून्य-सूचक में 0 असाइन करके वही काम किया (जो मैंने सोचा था)।

unsafe 
{ 
    *(int*)0 = 0; 
} 

कारणों के कारण मुझे यह NullReferenceException फेंकता है। मैंने इसके साथ कुछ खेला और पाया कि *(int*)1 का उपयोग करके NullReferenceException भी फेंकता है, लेकिन यदि आप नकारात्मक संख्या का उपयोग करते हैं, जैसे *(int*)-1 यह AccessViolationException फेंक देगा।

यहां क्या हो रहा है? *(int*)0 = 0 का कारण NullReferenceException क्यों है, और इसका कारण AccessViolationException क्यों नहीं है?

+1

'(int *) 0' एक शून्य सूचक है। मैं पूरी तरह से 'NullReferenceException' की अपेक्षा करता हूं। यदि आप 'AccessViolationException' चाहते हैं, तो' (int *) 0x10' (या संभवतः '0xf0000000') जैसी कुछ कोशिश करें। – cHao

+7

संभावित डुप्लिकेट [सबसे कम पता स्थान में स्मृति पहुंच क्यों है (गैर-शून्य हालांकि) .NET द्वारा NullReferenceException के रूप में रिपोर्ट की गई है?] (Http://stackoverflow.com/questions/7940492/why-is-memory-access-in -टेस्ट-एड्रेस-स्पेस-गैर-नल-यद्यपि-रिपोर्ट-ए-एन) –

उत्तर

28

एक शून्य संदर्भ अपवाद होता है जब आप एक शून्य सूचक को कम करते हैं; सीएलआर परवाह नहीं है कि नल पॉइंटर एक असुरक्षित पॉइंटर है जिसमें पूर्णांक शून्य में फंसे हुए हैं या एक प्रबंधित पॉइंटर (यानी संदर्भ प्रकार की वस्तु का संदर्भ है) शून्य में फंस गया है।

सीएलआर कैसे जानता है कि शून्य को संदर्भित किया गया है? और सीएलआर कैसे जानता है जब कुछ अन्य अमान्य सूचक को संदर्भित किया गया है? प्रक्रिया के वर्चुअल मेमोरी एड्रेस स्पेस में प्रत्येक पॉइंटर वर्चुअल मेमोरी के पेज में कहीं भी इंगित करता है। ऑपरेटिंग सिस्टम ट्रैक रखता है कि कौन से पृष्ठ वैध हैं और जो अमान्य हैं; जब आप किसी अमान्य पृष्ठ को स्पर्श करते हैं तो यह सीएलआर द्वारा पता लगाया गया अपवाद उठाता है। सीएलआर तब सतहों को एक अवैध पहुंच अपवाद या शून्य संदर्भ अपवाद के रूप में सतह पर रखता है।

यदि अमान्य पहुंच स्मृति के 64K के नीचे है, तो यह एक शून्य अपवाद है। अन्यथा यह एक अवैध पहुंच अपवाद है।

यह बताता है कि क्यों शून्य को संदर्भित करना और एक शून्य अपवाद को अपवाद देना है, और क्यों dereferencing -1 एक अवैध पहुंच अपवाद देता है; -1 32 बिट मशीनों पर पॉइंटर 0xFFFFFFFF है, और वह विशेष पृष्ठ (x86 मशीनों पर) हमेशा ऑपरेटिंग सिस्टम के लिए अपने उद्देश्यों के लिए उपयोग किया जाता है। उपयोगकर्ता कोड इसका उपयोग नहीं कर सकता है।

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

int* p = (int*)0; 
int x = p[1]; 

संकलक तब्दील हो कि की नैतिक बराबर में: जो 4. अपसंदर्भन है लेकिन उपयोगकर्ता के नजरिए से

int* p = (int*)0; 
int x = *((int*)((int)p + 1 * sizeof(int))); 

, p[1] निश्चित रूप से एक भिन्नता की तरह दिखता है शून्य की! तो वह त्रुटि है जो रिपोर्ट की गई है।

+0

शानदार (और बहुत रोचक) जवाब! +1 –

2

NullReferenceException कहा गया है कि "अपवाद है जब वहाँ एक अशक्त वस्तु संदर्भ भिन्नता का एक प्रयास है कि फेंक दिया जाता है", इसलिए बाद से *(int*)0 = 0 की कोशिश करता एक वस्तु का उपयोग कर भिन्नता यह एक NullReferenceException फेंक होगा स्मृति स्थान 0x000 स्थापित करने के लिए। ध्यान दें कि स्मृति को एक्सेस करने का प्रयास करने से पहले यह अपवाद फेंक दिया गया है।

दूसरी ओर राज्यों पर AccessViolationException वर्ग कि, "अपवाद है कि जब वहाँ पढ़ने के लिए या संरक्षित स्मृति लिखने के लिए एक प्रयास है फेंक दिया जाता है", और System.Runtime.InteropServices.Marshal.WriteInt32(IntPtr.Zero, 0) के बाद से एक भिन्नता का उपयोग नहीं करता, बजाय स्मृति सेट करने का प्रयास इस विधि का उपयोग करते हुए, किसी ऑब्जेक्ट को संदर्भित नहीं किया जाता है, इसलिए इसका अर्थ नहीं है NullReferenceException फेंक दिया जाएगा।

+1

यह पूरी तरह से सच नहीं है। 'WriteInt32' _does_ पॉइंटर को अस्वीकार करता है, लेकिन एनआरई को पकड़ता है और इसके बजाय एक एक्सेस उल्लंघन फेंकता है। – Daniel

+0

यह उत्तर गलत है। सही उत्तर यहां है: http://stackoverflow.com/a/7940659/162396 – Daniel

4

यह प्रति जवाब नहीं है, लेकिन यदि आप WriteInt32 को संकुचित करते हैं तो आपको लगता है कि यह NullReferenceException पकड़ता है और AccessViolationException फेंकता है। तो व्यवहार की संभावना एक जैसी है, लेकिन वास्तविक अपवाद को पकड़ा जा रहा है और एक अलग अपवाद उठाया जा रहा है।

1

MSDN का कहना है कि स्पष्ट रूप से:

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

AccessViolationException सहायता देखें।

+0

यह स्पष्ट नहीं करता कि दो स्निपेट अलग-अलग अपवाद क्यों फेंकते हैं। – Daniel

+0

डैनियल, वास्तव में यह समझा रहा है। मैंने इसे विस्तारित नहीं किया है क्योंकि JSPerfUnkn0wn से पहले ही विस्तार से स्पष्टीकरण है। – Yakeen

+0

डैनियल, हंस से स्पष्टीकरण जो आपको संदर्भ देता है उचित लगता है। – Yakeen

0

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