c++ - क्या एक सूचक को 0-आकार के गतिशील सरणी में अपरिभाषित किया गया है?




pointers undefined-behavior (2)

मुझे लगता है कि आपके पास पहले से ही जवाब है; यदि आप थोड़े गहरे दिखते हैं: आपने कहा है कि एक बंद चलने वाले itter को बढ़ाना UB है इस प्रकार: यह उत्तर क्या है जो एक पुनरावृत्त है?

इट्रेटर सिर्फ एक ऑब्जेक्ट है जिसमें एक पॉइंटर है और इंक्रीमेंट है कि इट्रेटर वास्तव में पॉइंटर को बढ़ाता है। इस प्रकार कई पहलुओं में एक सूचक के संदर्भ में एक पुनरावृत्ति संभाला जाता है।

int arr [] = {0,1,2,3,4,5,6,7,8,9};

int * p = arrest; // p पहले तत्व को गिरफ्तारी में इंगित करता है

++ पी; // p अंक गिरफ्तार करने के लिए [1]

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

int * e = & arrest [10]; // पॉइंटर गिरफ्तारी में अंतिम तत्व से ठीक पहले

यहाँ हमने सबस्क्रिप्ट ऑपरेटर का उपयोग एक नोक्सिस्टिंग तत्व को इंडेक्स करने के लिए किया; गिरफ्तारी में दस तत्व हैं, इसलिए गिरफ्तारी में अंतिम तत्व सूचकांक स्थिति 9 पर है। इस तत्व के साथ हम केवल एक चीज कर सकते हैं, इसका पता है, जिसे हम ई को इनिशियलाइज़ करने के लिए करते हैं। ऑफ-द-एंड इटेरेटर (, 3.4.1, पृष्ठ 106) की तरह, ऑफ-द-एंड पॉइंटर एक तत्व को इंगित नहीं करता है। नतीजतन, हम एक बंद-सूचक को अनुमेय या बढ़ा नहीं सकते हैं।

यह लिपमैन द्वारा सी ++ प्राइमर 5 संस्करण से है।

तो यह यूबी नहीं है।

AFAIK, हालांकि हम एक 0-स्थैतिक-स्मृति सरणी नहीं बना सकते हैं, लेकिन हम इसे गतिशील लोगों के साथ कर सकते हैं:

int a[0]{}; // Compile-time error
int* p = new int[0]; // Is well-defined

जैसा कि मैंने पढ़ा है, p एक-पास्ट-एंड तत्व की तरह काम करता है। मैं उस पते को प्रिंट कर सकता हूं जो p इंगित करता है।

if(p)
    cout << p << endl;
  • हालाँकि मुझे यकीन है कि हम उस सूचक (भूतकाल-अंतिम-तत्व) का विरोध नहीं कर सकते हैं, जैसा कि हम पुनरावृत्तियों (भूत-अंतिम तत्व) के साथ नहीं कर सकते हैं, लेकिन जो मुझे यकीन नहीं है कि क्या उस सूचक p बढ़ाना है? क्या पुनरावृत्तियों के साथ अपरिभाषित व्यवहार (UB) है?

    p++; // UB?

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

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

[expr.post.incr] / [expr.pre.incr]
ऑपरेंड पूरी तरह से परिभाषित ऑब्जेक्ट प्रकार के लिए [...] या एक सूचक होगा।

ओह, एक पल रुको, एक पूरी तरह से परिभाषित वस्तु प्रकार? बस इतना ही? मेरा मतलब है, वास्तव में, टाइप करें ? तो आपको किसी वस्तु की आवश्यकता नहीं है?
वास्तव में एक संकेत खोजने के लिए पढ़ने में काफी थोड़ा समय लगता है कि वहां कुछ ऐसा नहीं हो सकता है जो बहुत अच्छी तरह से परिभाषित हो। क्योंकि अब तक, यह पढ़ता है जैसे कि आपको इसे करने की पूरी तरह से अनुमति है, कोई प्रतिबंध नहीं।

[basic.compound] 3 यह बताता है कि किसी के पास किस प्रकार का पॉइंटर हो सकता है, और अन्य तीन में से कोई भी नहीं होने के कारण, आपके ऑपरेशन का परिणाम स्पष्ट रूप से 3.4: अमान्य पॉइंटर के तहत आएगा।
हालांकि यह नहीं कहता है कि आपको अमान्य सूचक रखने की अनुमति नहीं है। इसके विपरीत, यह कुछ बहुत ही सामान्य, सामान्य स्थितियों (जैसे भंडारण अवधि का अंत) को सूचीबद्ध करता है जहां संकेत नियमित रूप से अमान्य हो जाते हैं। तो यह स्पष्ट रूप से एक स्वीकार्य बात है। और सचमुच में:

[basic.stc] ४
अमान्य पॉइंटर मान के माध्यम से अप्रत्यक्ष और एक अमान्य फ़ंक्शन के लिए अमान्य पॉइंटर मान पास करने से अपरिभाषित व्यवहार होता है। अमान्य सूचक मान के किसी अन्य उपयोग में कार्यान्वयन-परिभाषित व्यवहार होता है।

हम वहां "कोई अन्य" कर रहे हैं, इसलिए यह अपरिभाषित व्यवहार नहीं है, लेकिन कार्यान्वयन-परिभाषित है, इस प्रकार आम तौर पर स्वीकार्य है (जब तक कि कार्यान्वयन स्पष्ट रूप से कुछ अलग नहीं कहता है)।

दुर्भाग्य से, यह कहानी का अंत नहीं है। हालांकि शुद्ध परिणाम यहाँ से किसी भी अधिक नहीं बदलता है, यह अधिक भ्रमित हो जाता है, अब आप "पॉइंटर" के लिए खोज करते हैं:

[Basic.compound]
ऑब्जेक्ट पॉइंटर प्रकार का एक मान्य मान या तो मेमोरी में बाइट या अशक्त पॉइंटर के पते को दर्शाता है। यदि टाइप ए की एक वस्तु एक पते पर स्थित है [...] तो उस वस्तु को इंगित करने के लिए कहा जाता है, भले ही मूल्य कैसे प्राप्त किया गया हो
[नोट: उदाहरण के लिए, एक सरणी के अंत में पिछले एक पते को उस सरणी के तत्व प्रकार के असंबंधित ऑब्जेक्ट को इंगित करने के लिए माना जाएगा जो उस पते पर स्थित हो सकता है। [...]]।

पढ़ें: ठीक है, कौन परवाह करता है! जब तक कोई पॉइंटर मेमोरी में कहीं इंगित करता है , मैं अच्छा हूं?

[basic.stc.dynamic.safety] एक पॉइंटर मान एक सुरक्षित रूप से व्युत्पन्न पॉइंटर [ब्ला ब्ला] है

के रूप में पढ़ें: ठीक है, सुरक्षित रूप से व्युत्पन्न, जो भी हो। यह नहीं समझाता है कि यह क्या है, और न ही यह कहता है कि मुझे वास्तव में इसकी आवश्यकता है। सुरक्षित रूप से व्युत्पन्न-बिल्ली। जाहिरा तौर पर मैं अभी भी गैर-सुरक्षित-व्युत्पन्न संकेत ठीक कर सकता हूं। मैं यह अनुमान लगा रहा हूं कि शायद उन्हें ऐसा करने का कोई विचार नहीं होगा, लेकिन यह उनके लिए पूरी तरह से स्वीकार्य है। यह अन्यथा नहीं कहता है।

एक कार्यान्वयन में पॉइल्टर सेफ्टी की सुरक्षा हो सकती है, ऐसे में पॉइंटर वैल्यू की वैधता इस बात पर निर्भर नहीं करती है कि यह सुरक्षित रूप से प्राप्त पॉइंटर वैल्यू है या नहीं।

ओह, तो यह बात नहीं हो सकती है, बस मैंने जो सोचा था। लेकिन रुकिए ... "हो सकता है" नहीं? इसका मतलब है, यह भी हो सकता है । मुझे कैसे पता चलेगा?

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

रुको, तो यह भी संभव है कि मुझे हर सूचक पर declare_reachable() कॉल करने की आवश्यकता है? मुझे कैसे पता चलेगा?

अब, आप intptr_t बदल सकते हैं, जो कि एक अच्छी तरह से परिभाषित है, एक सुरक्षित रूप से व्युत्पन्न सूचक का पूर्णांक प्रतिनिधित्व देता है। जिसके लिए, बेशक, पूर्णांक होने के नाते, यह पूरी तरह से वैध और अच्छी तरह से परिभाषित है जैसा कि आप चाहते हैं।
और हाँ, आप intptr_t को पॉइंटर में बदल सकते हैं, जो अच्छी तरह से परिभाषित भी है। केवल, मूल मूल्य नहीं होने के कारण, यह अब गारंटी नहीं है कि आपके पास सुरक्षित रूप से व्युत्पन्न सूचक (स्पष्ट रूप से) है। फिर भी, सभी में, मानक के अक्षर तक, कार्यान्वयन-परिभाषित होने के दौरान, यह करने के लिए एक 100% वैध बात है:

[expr.reinterpret.cast] ५
अभिन्न प्रकार या गणना प्रकार का मान स्पष्ट रूप से एक सूचक में परिवर्तित किया जा सकता है। एक पॉइंटर पर्याप्त आकार के पूर्णांक में परिवर्तित हो जाता है [...] और उसी पॉइंटर प्रकार पर वापस जाता है [...] मूल मूल्य; संकेत और पूर्णांक के बीच मैपिंग अन्यथा कार्यान्वयन-परिभाषित हैं।

कैच

पॉइंटर्स केवल साधारण पूर्णांक हैं, केवल आप उन्हें पॉइंटर्स के रूप में उपयोग करने के लिए होते हैं। ओह अगर केवल यह सच था!
दुर्भाग्य से, ऐसे आर्किटेक्चर मौजूद हैं जहां यह बिल्कुल सच नहीं है, और केवल एक अमान्य पॉइंटर उत्पन्न कर रहा है (इसे डीरेफेरेंस नहीं कर रहा है, बस एक पॉइंटर रजिस्टर में होने के कारण) एक जाल का कारण होगा।

तो यह "कार्यान्वयन परिभाषित" का आधार है। कि, और तथ्य यह है कि जब भी आप चाहते हैं कि एक संकेतक बढ़ाना, जैसा कि आप कृपया निश्चित रूप से अतिप्रवाह का कारण बन सकते हैं, जो मानक से निपटना नहीं चाहता है। अनुप्रयोग पता स्थान का अंत अतिप्रवाह के स्थान के साथ मेल नहीं खा सकता है, और आपको यह भी नहीं पता है कि किसी विशेष वास्तुकला पर संकेत के लिए अतिप्रवाह जैसी कोई चीज है या नहीं। सभी सभी में यह एक बुरे सपने है संभावित लाभों के किसी भी संबंध में नहीं।

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





dynamic-arrays