C या C ++ में "नल चेक" करने का क्या मतलब है?


21

मैं C ++ सीख रहा हूं और मुझे अशक्त समझने में कठिन समय हो रहा है। विशेष रूप से, मैंने जिस ट्यूटोरियल को पढ़ा है, वह "शून्य जांच" कर रहा है, लेकिन मुझे यकीन नहीं है कि इसका क्या मतलब है या यह क्यों आवश्यक है।

  • क्या वास्तव में अशक्त है?
  • "नल के लिए जांच" का क्या मतलब है?
  • क्या मुझे हमेशा अशक्त रहने की आवश्यकता है?

किसी भी कोड उदाहरण बहुत सराहना की जाएगी।



मैं आपको कुछ बेहतर ट्यूटोरियल प्राप्त करने की सलाह दूंगा, यदि आप जो भी पढ़ते हैं, वे बिना किसी को बताए, बिना किसी को बताए और उदाहरण कोड प्रदान किए हुए ...
अंडरस्कोर_ड

जवाबों:


26

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

एक सामान्य समाधान जो कई त्रुटियों को पकड़ता है, वह सभी बिंदुओं को सेट करने के लिए है जो NULL(या, सही C ++ में 0) के रूप में कुछ भी इंगित नहीं करता है , और पॉइंटर तक पहुंचने से पहले उसके लिए जांच कर रहा है। विशेष रूप से, सभी पॉइंटर्स को NULL (जब तक कि आपके पास उन्हें घोषित करने के लिए पहले से ही कुछ नहीं है) को आरंभ करने के लिए यह आम बात है, और उन्हें NULL के लिए सेट करें जब आप deleteया free()उन्हें (जब तक कि वे उसके तुरंत बाद दायरे से बाहर न निकल जाएं)। उदाहरण (C में, लेकिन मान्य C ++):

void fill_foo(int* foo) {
    *foo = 23; // this will crash and burn if foo is NULL
}

एक बेहतर संस्करण:

void fill_foo(int* foo) {
    if (!foo) { // this is the NULL check
        printf("This is wrong\n");
        return;
    }
    *foo = 23;
}

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


3
@Mrister आप क्या मतलब है, शून्य चेक C ++ में काम नहीं करते? जब आप इसे घोषित करते हैं तो आपको सूचक को शून्य करने के लिए आरंभ करना होगा।
TZHX

1
मेरा क्या मतलब है, आपको सूचक को NULL पर सेट करना याद रखना चाहिए या यह काम नहीं करेगा। और अगर आपको याद है, दूसरे शब्दों में अगर आपको पता है कि पॉइंटर NULL है, तो आपको फिर भी fill_foo पर कॉल करने की आवश्यकता नहीं होगी। fill_foo जांच करता है कि सूचक एक मूल्य है, सूचक एक है नहीं तो मान्य मूल्य। C ++ में, पॉइंटर्स के पास वैध वैल्यू के NULL होने की गारंटी नहीं है।
श्री लिस्टर

4
एक मुखर () यहाँ एक बेहतर समाधान होगा। "सुरक्षित रहने" की कोशिश करने का कोई मतलब नहीं है। यदि NULL को पारित किया गया था, तो यह स्पष्ट रूप से गलत है, इसलिए प्रोग्रामर को पूरी तरह से अवगत कराने के लिए स्पष्ट रूप से दुर्घटनाग्रस्त क्यों न हो? (और उत्पादन में, इससे कोई फर्क नहीं पड़ता, क्योंकि आपने साबित कर दिया है कि कोई भी fill_foo () को NULL के साथ नहीं कहेगा; ठीक है? वास्तव में, यह इतना कठिन नहीं है।)
अंबोज़ बिज्जक

7
यह उल्लेख करना न भूलें कि इस फ़ंक्शन का एक बेहतर संस्करण पॉइंटर्स के बजाय संदर्भ का उपयोग करना चाहिए, जिससे NULL चेक अप्रचलित हो जाएगा।
डॉक ब्राउन

4
यह वह नहीं है जो मैनुअल मेमोरी प्रबंधन के बारे में है, और एक प्रबंधित प्रोग्राम भी उड़ जाएगा, (या एक अपवाद को कम से कम उठाएं, जैसे कि एक मूल कार्यक्रम अधिकांश भाषाओं में होगा,) यदि आप एक अशक्त संदर्भ को रोकने की कोशिश करते हैं।
मेसन व्हीलर

7

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

मैं अपनी सलाह का एक टुकड़ा यहाँ जोड़ने जा रहा हूँ, हालाँकि। अशक्त जाँच से बचें। :) नल चेक (और रक्षात्मक प्रोग्रामिंग के अन्य रूपों) अव्यवस्था कोड अप, और वास्तव में यह अन्य त्रुटि से निपटने की तकनीक की तुलना में अधिक त्रुटि प्रवण बनाते हैं।

ऑब्जेक्ट पॉइंटर्स की बात करते समय मेरी पसंदीदा तकनीक नल ऑब्जेक्ट पैटर्न का उपयोग करना है । इसका मतलब है कि एक (पॉइंटर - या उससे भी बेहतर, वापसी के संदर्भ में) रिक्त सरणी या सूची की बजाय अशक्त, या रिक्त स्ट्रिंग ("") को वापस करने के बजाय अशक्त, या यहां तक ​​कि स्ट्रिंग "0" (या "कुछ भी नहीं के बराबर) "संदर्भ में) जहां आप यह अपेक्षा करते हैं कि इसे पूर्णांक में पार्स किया जाए।"

एक बोनस के रूप में, यहाँ कुछ ऐसा है जो आप अशक्त सूचक के बारे में नहीं जानते होंगे, जो 1965 में Algol W भाषा के लिए CAR Hoare द्वारा पहली बार औपचारिक रूप से लागू किया गया था।

मैं इसे अपनी अरबों डॉलर की गलती कहता हूं। यह 1965 में अशक्त संदर्भ का आविष्कार था। उस समय, मैं ऑब्जेक्ट ओरिएंटेड भाषा (ALGOL W) में संदर्भ के लिए पहला व्यापक प्रकार का सिस्टम डिजाइन कर रहा था। मेरा लक्ष्य यह सुनिश्चित करना था कि संकलक द्वारा स्वचालित रूप से प्रदर्शन किए जाने के साथ सभी संदर्भों का उपयोग पूरी तरह से सुरक्षित होना चाहिए। लेकिन मैं एक अशक्त संदर्भ में डालने के लिए प्रलोभन का विरोध नहीं कर सका, सिर्फ इसलिए कि इसे लागू करना इतना आसान था। इससे असंख्य त्रुटियां, कमजोरियां और सिस्टम क्रैश हो गए हैं, जिसने पिछले चालीस वर्षों में संभवतः एक अरब डॉलर का दर्द और क्षति पहुंचाई है।


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

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

अगर एल्गोरिथ्म में कुछ नहीं करना है, तो आप इसे क्यों बुला रहे हैं? और कारण जिसे आप पहली बार में कॉल करना चाहते हैं, वह है क्योंकि यह कुछ महत्वपूर्ण काम करता है
17-16 बजे डेडएमजी

@DeadMG क्योंकि प्रोग्राम इनपुट के बारे में हैं, और वास्तविक दुनिया में, होमवर्क असाइनमेंट के विपरीत, इनपुट अप्रासंगिक हो सकता है (उदाहरण के लिए खाली)। कोड को अभी भी रास्ता कहा जाता है। आपके पास दो विकल्प हैं: या तो आप प्रासंगिकता (या शून्यता) के लिए जांच करें, या आप अपने एल्गोरिदम को डिजाइन करें ताकि वे सशर्त बयानों का उपयोग करके प्रासंगिकता के लिए स्पष्ट रूप से जांच किए बिना पढ़ सकें और काम कर सकें।
यम मारकोविच

मैं यहाँ लगभग एक ही टिप्पणी करने के लिए आया था, इसलिए आपने इसके बदले मुझे अपना वोट दिया। हालाँकि, मैं यह भी जोड़ूंगा कि यह एक बड़ी समस्या का प्रतिनिधि है ज़ोंबी ऑब्जेक्ट्स - कभी भी आपके पास मल्टी-स्टेज इनिशियलाइज़ेशन (या विनाश) के साथ ऑब्जेक्ट्स होते हैं जो पूरी तरह से लाइव नहीं होते हैं लेकिन काफी मृत होते हैं। जब आप निर्धारक अंतिमकरण के बिना भाषाओं में "सुरक्षित" कोड देखते हैं, जिसने प्रत्येक फ़ंक्शन में चेक को यह देखने के लिए जोड़ा है कि क्या वस्तु का निपटान किया गया है, तो यह इस सामान्य समस्या का पालन कर रहा है। आपको कभी भी अशक्त नहीं होना चाहिए, आपको उन राज्यों के साथ काम करना चाहिए जिनके पास उनके जीवनकाल के लिए आवश्यक वस्तुएं हैं।
ex0du5

4

अशक्त सूचक मान एक अच्छी तरह से परिभाषित "कहीं नहीं" का प्रतिनिधित्व करता है; यह एक अमान्य सूचक मान है जो किसी अन्य सूचक मान के लिए असमान की तुलना करने की गारंटी है। एक अनिर्णायक व्यवहार में एक शून्य सूचक परिणाम को रोकने की कोशिश कर रहा है, और आमतौर पर एक रनटाइम त्रुटि का कारण होगा, इसलिए आप यह सुनिश्चित करना चाहते हैं कि एक सूचक इसे बाधित करने का प्रयास करने से पहले NULL नहीं है। C और C ++ लाइब्रेरी फ़ंक्शंस की एक संख्या एक त्रुटि स्थिति को इंगित करने के लिए अशक्त सूचक लौटाएगी। उदाहरण के लिए, लाइब्रेरी फ़ंक्शन mallocएक अशक्त सूचक मान लौटाएगा यदि यह अनुरोध किए गए बाइट्स की संख्या को आवंटित नहीं कर सकता है, और उस पॉइंटर के माध्यम से मेमोरी तक पहुंचने का प्रयास (आमतौर पर) एक रनटाइम त्रुटि का कारण होगा:

int *p = malloc(sizeof *p * N);
p[0] = ...; // this will (usually) blow up if malloc returned NULL

इसलिए हमें यह सुनिश्चित करने की आवश्यकता है कि NULL mallocके pविरुद्ध मूल्य की जाँच करके कॉल सफल हुई :

int *p = malloc(sizeof *p * N);
if (p != NULL) // or just if (p)
  p[0] = ...;

अब, अपने मोजे को एक मिनट के लिए लटकाएं, इससे थोड़ा ऊब होने वाला है।

एक अशक्त सूचक है मान और एक अशक्त सूचक स्थिरांक है , और दो जरूरी समान नहीं हैं। अशक्त सूचक मान वह है जो "आर्किटेक्चर" का प्रतिनिधित्व करने के लिए अंतर्निहित वास्तुकला का उपयोग करता है। यह मान 0x00000000, या 0xFFFFFFFF, या 0xDEADBEEF, या कुछ पूरी तरह से अलग हो सकता है। यह मत समझो कि अशक्त सूचक मान हमेशा 0 होता है।

अशक्त सूचक स्थिर , OTOH, हमेशा 0-मूल्यवान अभिन्न अभिव्यक्ति है। जहाँ तक आपके स्रोत कोड का संबंध है, 0 (या किसी भी अभिन्न अभिव्यक्ति जो 0 का मूल्यांकन करता है) एक अशक्त सूचक का प्रतिनिधित्व करता है। C और C ++ दोनों NULL मैक्रो को नल पॉइंटर स्थिर के रूप में परिभाषित करते हैं। जब आपका कोड संकलित किया जाता है, तो null पॉइंटर स्थिरांक को जनरेट मशीन कोड में उपयुक्त null पॉइंटर मान से बदल दिया जाएगा ।

इसके अलावा, ध्यान रखें कि NULL कई संभावित अमान्य पॉइंटर मानों में से केवल एक है ; यदि आप स्पष्ट रूप से इसे शुरू करने के बिना एक ऑटो पॉइंटर चर घोषित करते हैं, जैसे कि

int *p;

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

ध्यान दें कि यह C ++ की तुलना में C में एक समस्या है; मुहावरेदार C ++ को पॉइंटर्स का उपयोग नहीं करना चाहिए।


3

कुछ तरीके हैं, सभी अनिवार्य रूप से एक ही काम करते हैं।

int * foo = NULL; // कभी-कभी NULL के बजाय 0x00 या 0 या 0L पर सेट करें

null चेक (पॉइंटर null है तो चेक करें), संस्करण A

अगर (foo == NULL)

नल की जांच, संस्करण बी

अगर (! foo) // चूंकि NULL को 0 के रूप में परिभाषित किया गया है, तो full एक शून्य सूचक से मान लौटाएगा

अशक्त जांच, संस्करण सी

अगर (foo == 0)

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


2

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


1
@ नाम: कर्नेल मोड में 'नया'?
नेमेनजा ट्रिफ़ुनोविक

1
@ नाम: C ++ का कार्यान्वयन जो उन क्षमताओं का प्रतिनिधित्व करता है जो C ++ कोडर्स का एक महत्वपूर्ण हिस्सा आनंद लेते हैं। जिसमें सभी C ++ 03 भाषा सुविधाएँ (छोड़कर export) और सभी C ++ 03 लाइब्रेरी सुविधाएँ और TR1 और C ++ 11 का एक अच्छा हिस्सा शामिल हैं।
डेडएमजी

5
मैं करना चाहते हैं लोगों को नहीं कहूँगा कि "संदर्भ की गारंटी में गैर-शून्य।" वे नहीं करते। अशक्त सूचक के रूप में अशक्त संदर्भ उत्पन्न करना उतना ही आसान है, और वे उसी तरह प्रचारित करते हैं।
mjfgates

2
@Stargazer: सवाल 100% बेमानी है जब आप उपकरण का उपयोग करते हैं जिस तरह से भाषा डिजाइनर और अच्छा अभ्यास आपको सुझाव देना चाहिए।
डेडएमजी

2
@DeadMG, यह कोई बात नहीं है कि क्या यह बेमानी है। आपने सवाल का जवाब नहीं दिया । मैं इसे फिर से कहूँगा: -1।
रिवालक

-1

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

Microsoft, Oracle, Adobe, Apple ... जैसे कई सॉफ्टवेयर विक्रेता इन सुरक्षा कमजोरियों को ठीक करने के लिए सॉफ्टवेयर पैच जारी करते हैं। मुझे लगता है कि आपको प्रत्येक पॉइंटर का पूरा मान चेक करना चाहिए :)

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