2010-04-28 8 views
15

सी ++ मानक (3.7.3.2/4) (न केवल ड्रेफ्रेंसिंग, बल्कि प्रतिलिपि, कास्टिंग, और भी कुछ) का उपयोग करते हुए एक अमान्य सूचक अनिर्धारित व्यवहार है (संदेह के मामले में this question देखें)। अब एक एसटीएल containter पार करने के लिए ठेठ कोड इस तरह दिखता है:"एंड()" इटरेटर कानूनी के खिलाफ तुलना क्यों कर रहा है?

std::vector<int> toTraverse; 
//populate the vector 
for(std::vector<int>::iterator it = toTraverse.begin(); it != toTraverse.end(); ++it) { 
    //process(*it); 
} 

std::vector::end() प्राक्कल्पित तत्व पर पुनरावर्तक containter के अंतिम तत्व से परे है। वहां कोई तत्व नहीं है, इसलिए उस इटरेटर के माध्यम से एक पॉइंटर का उपयोग अपरिभाषित व्यवहार है।

अब != end() कैसे काम करता है? मेरा मतलब यह है कि तुलना करने के लिए एक इटरेटर को एक अमान्य पते को लपेटने के लिए बनाया जाना चाहिए और फिर उस अमान्य पते को एक तुलना में उपयोग किया जाना चाहिए जो फिर से अपरिभाषित व्यवहार है। क्या ऐसी तुलना कानूनी है और क्यों?

+0

3.7.3.2/4 यह नहीं कहता कि एक अमान्य सूचक की प्रतिलिपि बनाना और कास्टिंग करना यूबी है। मेरा मानना ​​है कि आपकी व्याख्या बहुत व्यापक है। –

+0

@ किरील वी। लिडविंस्की: शायद, लेकिन यह लिंक किए गए प्रश्न का मूल है जहां आम सहमति यह है कि अमान्य पॉइंटर्स कास्टिंग और असाइन करना यूबी है। – sharptooth

उत्तर

9

आप सही हैं कि एक अवैध सूचक का उपयोग नहीं किया जा सकता है, लेकिन आप गलत हैं कि एक सरणी में अंतिम तत्व से पहले तत्व के लिए एक पॉइंटर एक अमान्य सूचक है - यह मान्य है।

सी मानक, खंड 6.5.6.8 का कहना है कि यह अच्छी तरह से परिभाषित किया है और वैध:

... अगर एक सरणी वस्तु के पिछले तत्व को अभिव्यक्ति पी अंक, अभिव्यक्ति (पी) +1 अंक सरणी वस्तु के अंतिम तत्व के बाद एक ...

लेकिन dereferenced नहीं किया जा सकता:

...

+2

अंतिम उद्धरण सी ++ के बारे में सच नहीं है। यदि आप जानते हैं कि सरणी के बाद तत्व प्रकार का एक अन्य ऑब्जेक्ट रहता है (जैसा कि एक मल्टीडिम सरणी में है) तो आप * इसे * अस्वीकार कर सकते हैं। –

+0

क्या आपके पास इसके लिए कोई संदर्भ है और क्या यह सी ++ में मान्य है लेकिन सी नहीं? –

+2

सी ++ में यू (यूबी नहीं) और सी में यूबी मान्य है, हां। लेकिन केवल अगर उस स्थिति में वास्तव में एक वस्तु है। '5.7/5' और' 3.9.2/3' देखें। –

3

हू? ऐसा कोई नियम नहीं है जो कहता है कि इटरेटर को पॉइंटर के अलावा कुछ भी नहीं इस्तेमाल करने की आवश्यकता है।

इसमें वहां एक बूलियन ध्वज हो सकता है, जो तब बढ़ता है जब वृद्धि ऑपरेशन देखता है कि यह वैध डेटा के अंत को पास करता है, उदाहरण के लिए।

24

end() के लिए एकमात्र आवश्यकता ++(--end()) == end() है। end() बस एक विशेष स्थिति हो सकता है जो इटेटरेटर में है। end() इटेटर को किसी भी प्रकार के सूचक से मेल खाने का कोई कारण नहीं है।

इसके अलावा, भले ही यह एक सूचक था, दो बिंदुओं की तुलना करने के लिए किसी भी तरह की अव्यवस्था की आवश्यकता नहीं होती है। निम्नलिखित पर विचार करें:

char[5] a = {'a', 'b', 'c', 'd', 'e'}; 
char* end = a+5; 
for (char* it = a; it != a+5; ++it); 

वह कोड ठीक काम करेगा, और यह आपके वेक्टर कोड को प्रतिबिंबित करेगा।

+0

यह कहता है कि मेरे उत्तर से बेहतर है। मुझसे +1 – sbi

+0

@ निक लुईस: मैं अन्य बिंदुओं के खिलाफ बहस नहीं करूंगा, लेकिन मानक कहता है कि एक अवैध सूचक का उपयोग भी यूबी है, इसलिए 'char * end = a + 5;' यूबी है। – sharptooth

+13

@ शार्पतोथ: सरणी का एक पिछला अंत ** ** एक अमान्य सूचक नहीं है। – UncleBens

1

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

+1

कोई चाल आवश्यक नहीं है - अंतिम तत्व से पहले एक वैध सूचक है जिसे अस्वीकृत नहीं किया जा सकता है। –

+1

@ जो: मैंने यह नहीं कहा कि चालें _required_ थीं। मैंने कहा कि कार्यान्वयन _can_ play चालें। (और एक सूची के लिए एक अतीत-द-एंड इटेटरेटर रखने का प्रयास करें।) इसलिए मुझे यकीन नहीं है कि डाउन-वोट क्या है। – sbi

+0

सवाल यह है कि किसी सरणी के अंत से पहले एक पॉइंटर कानूनी रूप से उपयोग क्यों किया जा सकता है, आपके उत्तर का तात्पर्य है कि 'end()' केवल परिभाषित परिभाषित चाल के कारण मान्य है। –

1

इसके अलावा क्या पहले से ही कहा गया था (iterators संकेत होने की ज़रूरत नहीं), मैं नियम आप का हवाला देते

सी के अनुसार बाहर बिंदु करना चाहते हैं ++ मानक (3.7.3.2/4) का उपयोग कर (न केवल अपसंदर्भन, लेकिन भी कॉपी करने, कास्टिंग, और जो कुछ भी) गलत सूचक अपरिभाषित व्यवहार

है वैसे भी end() इटरेटर के लिए लागू नहीं होगा। असल में, जब आपके पास एक सरणी होती है, तो उसके तत्वों के सभी पॉइंटर्स, साथ ही एक पॉइंटर अतीत-अंत-अंत में, सरणी की शुरुआत से पहले एक पॉइंटर मान्य होते हैं। इसका मतलब है:

int arr[5]; 
int *p=0; 
p==arr+4; // OK 
p==arr+5; // past-the-end, but OK 
p==arr-1; // also OK 
p==arr+123456; // not OK, according to your rule 
+0

विशेष रूप से "पहले से पहले" और "आखिरी" पॉइंटर्स वैध क्यों हैं? – sharptooth

+5

'पी == एआर -1; 'अपरिभाषित व्यवहार का आह्वान करता है (" यदि दोनों सूचक ऑपरेंड और परिणाम उसी सरणी ऑब्जेक्ट के तत्वों को इंगित करते हैं, या सरणी ऑब्जेक्ट के अंतिम तत्व से पहले, मूल्यांकन एक उत्पादन नहीं करेगा ओवरफ्लो; अन्यथा, व्यवहार अपरिभाषित है। ") –

1

सरल। Iterators (आवश्यक) पॉइंटर्स नहीं हैं।

उनके पास कुछ समानताएं हैं (यानी।आप उन्हें अपमानित कर सकते हैं), लेकिन यह इसके बारे में है।

4

अंत में एक अमान्य एक अमान्य मान नहीं है (न ही नियमित सरणी या इटरेटर के साथ)। आप इसे अस्वीकार नहीं कर सकते हैं लेकिन इसका उपयोग तुलना के लिए किया जा सकता है।

std::vector<X>::iterator it; 

यह एकवचन इटरेटर है। आप केवल एक वैध इटरेटर ही असाइन कर सकते हैं।

std::vector<X>::iterator it = vec.end(); 

यह एक पूरी तरह से मान्य इटरेटर है। आप इसे अस्वीकार नहीं कर सकते हैं लेकिन आप इसे तुलना के लिए उपयोग कर सकते हैं और इसे कम कर सकते हैं (माना जाता है कि कंटेनर का पर्याप्त आकार है)।

+0

" अंत में एक अंत "वैध क्यों है? – sharptooth

+0

सी मानक की धारा 6.5.6.8 स्पष्ट रूप से इसे अनुमति देता है। –

+0

@ शार्पतोथ: कई जगहों पर सरणी के अंत में एक के पते की तुलना करने की वैधता के बारे में मानक वार्ता। कल्पना करें कि यह मामला नहीं था - आप इसका उपयोग करने में सक्षम नहीं होंगे! = लूपिंग, प्रतिलिपि, आदि के दौरान सरणी के अंत का पता लगाने के लिए जो बहुत कठिन होगा। यद्यपि अंत में एक पूर्ववत करने के लिए यह अमान्य है। – markh44

0

मैं यहाँ के बाद से जवाब देने यदि परिणाम अंक सरणी वस्तु के अंतिम तत्व के बाद एक, यह एक एकल * ऑपरेटर कि मूल्यांकन किया जाता है की संकार्य के रूप में इस्तेमाल नहीं किया जाएगा ... अन्य उत्तर अब पुराने हैं; फिर भी, वे सवाल का सही नहीं थे।

सबसे पहले, सी ++ 14 ने प्रश्न में उल्लिखित नियमों को बदल दिया है। एक अमान्य सूचक मूल्य के माध्यम से संकेत या एक डेलोकेशन फ़ंक्शन के लिए अमान्य सूचक मान को पास करना अभी भी अनिर्धारित है, लेकिन अन्य परिचालन अब लागू नहीं हैं, Documentation of "invalid pointer value" conversion in C++ implementations देखें।

दूसरा, शब्द गिना जाता है। नियमों को लागू करते समय आप परिभाषाओं को बाईपास नहीं कर सकते हैं। यहां मुख्य बिंदु "अमान्य" की परिभाषा है। Iterators के लिए, यह [iterator.requirements] में परिभाषित किया गया है। वास्तव में, यह भी सच है कि pointers are iterators, उनके लिए "अमान्य" का अर्थ काफी अलग है। पॉइंटर्स के नियम "अमान्य" को "अमान्य मान के माध्यम से अप्रत्यक्ष नहीं करते" के रूप में प्रस्तुत करते हैं, जो कि "dereferenceable" का एक विशेष मामला है। हालांकि, "भरोसेमंद नहीं है" इटरेटर के लिए "अमान्य" का अर्थ है। "अमान्य" को स्पष्ट रूप से "may be singular" के रूप में परिभाषित किया गया है, जबकि "एकवचन" मान को "किसी भी अनुक्रम से संबद्ध नहीं" के रूप में परिभाषित किया गया है ("अस्वीकार्य" की परिभाषा के समान पैराग्राफ में)। उस पैराग्राफ ने स्पष्ट रूप से "अतीत के अंत मूल्य" को भी परिभाषित किया है।

[iterator.requirements] में मानक के पाठ से, यह स्पष्ट है कि:

  • विगत अंत मान dereferenceable (कम से कम मानक पुस्तकालय से) माना नहीं कर रहे हैं, के रूप में मानक राज्यों।
  • भरोसेमंद मान एकवचन नहीं हैं, क्योंकि वे अनुक्रम से जुड़े हुए हैं।
  • पिछले-अंत-अंत मूल्य एकवचन नहीं हैं, क्योंकि वे अनुक्रम से जुड़े हुए हैं।
  • यदि यह निश्चित रूप से एकवचन नहीं है ("अवैध इटरेटर" की परिभाषा पर अस्वीकार करके) एक पुनरावर्तक अमान्य नहीं है। दूसरे शब्दों में, यदि कोई पुनरावृत्ति अनुक्रम से जुड़ा हुआ है, तो यह अमान्य नहीं है।

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

ऐसे मानों पर == तुलना करने वाले नियम input iterator requirements में हैं, जो किसी अन्य श्रेणी के इटरेटर (आगे, द्विपक्षीय, आदि) द्वारा विरासत में प्राप्त होता है। अधिक विशेष रूप से, वैध iterators are required to be comparable in the domain of the iterator इस तरह से (==)। इसके अलावा, अग्रेषण आवश्यकताओं को आगे the domain is over the underlying sequence निर्दिष्ट करता है। और कंटेनर आवश्यकताएं iterator और const_iterator सदस्य प्रकार in any iterator category meets forward iterator requirements निर्दिष्ट करती हैं। इस प्रकार, ==end() पर और उसी कंटेनर पर इटरेटर को अच्छी तरह से परिभाषित करने की आवश्यकता है। एक मानक कंटेनर के रूप में, vector<int> भी आवश्यकताओं का पालन करता है। यह पूरी कहानी है।

तीसरा, यहां तक ​​कि end() एक सूचक मूल्य है (यह vector उदाहरण के इटरेटर के अनुकूलित कार्यान्वयन के साथ होने की संभावना है), प्रश्न में नियम अभी भी लागू नहीं हैं। कारण ऊपर उल्लिखित कारण है (और कुछ अन्य उत्तरों में): "अमान्य" * (अप्रत्यक्ष माध्यम से) से संबंधित है, तुलना नहीं। One-past-end value is explicitly allowed to be compared in specified ways by the standard. यह भी ध्यान दें कि आईएसओ सी ++ आईएसओ सी नहीं है, वे भी मिस्चैच को घटाते हैं (उदा। < के लिए पॉइंटर वैल्यू पर, उसी सरणी में नहीं, अनिर्दिष्ट बनाम अपरिभाषित), हालांकि उनके यहां समान नियम हैं।

संबंधित मुद्दे