एक फ्लैट मेमोरी मॉडल (मूल रूप से सब कुछ) के साथ कार्यान्वयन पर, uintptr_t
बस काम करना होगा।
(लेकिन यह देखना चाहिए कि 64-बिट x86 में पॉइंटर तुलनाओं पर हस्ताक्षर किए गए या अहस्ताक्षरित होने चाहिए , इस बात की चर्चा के लिए कि क्या आपको साइन इन के रूप में संकेत करना चाहिए या नहीं, जिसमें वस्तुओं के बाहर पॉइंटर्स बनाने के मुद्दे शामिल हैं जो सी में यूबी है।)
लेकिन गैर फ्लैट स्मृति मॉडल के साथ सिस्टम मौजूद हैं, और उनके बारे में सोच मदद कर सकते हैं वर्तमान स्थिति स्पष्ट, सी की तरह ++ के लिए विभिन्न चश्मा होने <
बनाम std::less
।
बिंदुओं के बिंदु का एक हिस्सा <
C में UB होने वाली वस्तुओं को अलग करने के लिए (या कुछ C ++ संशोधनों में कम से कम अनिर्दिष्ट) गैर-फ्लैट मेमोरी मॉडल सहित अजीब मशीनों के लिए अनुमति देने के लिए है।
एक प्रसिद्ध उदाहरण x86-16 वास्तविक मोड है जहां संकेत खंड हैं: ऑफसेट, 20-बिट रैखिक पते के माध्यम से बनाते हैं (segment << 4) + offset
। एक ही रेखीय पते को कई अलग-अलग seg: ऑफ कॉम्बिनेशन द्वारा दर्शाया जा सकता है।
std::less
अजीब आईएसए पर पॉइंटर्स पर सी ++ को महंगा होने की आवश्यकता हो सकती है , उदाहरण के लिए एक सेगमेंट को "सामान्य" करें: ऑफसेट करने के लिए x86-16 पर ऑफसेट <= 15. हालांकि, इसे लागू करने का कोई पोर्टेबल तरीका नहीं है । एक uintptr_t
(या पॉइंटर ऑब्जेक्ट के ऑब्जेक्ट-प्रतिनिधित्व) को सामान्य करने के लिए आवश्यक हेरफेर कार्यान्वयन-विशिष्ट है।
लेकिन यहां तक कि सिस्टम पर जहां C ++ std::less
महंगा होना चाहिए, <
नहीं होना चाहिए। उदाहरण के लिए, एक "बड़े" मेमोरी मॉडल को मानते हुए, जहां कोई वस्तु एक सेगमेंट के भीतर फिट होती है, <
बस ऑफसेट भाग की तुलना कर सकती है और खंड भाग के साथ परेशान भी नहीं कर सकती है। (एक ही ऑब्जेक्ट के अंदर पॉइंटर्स का एक ही सेगमेंट होगा, और अन्यथा यह सी। सी। + + 17 में यूबी है जो केवल "अनिर्दिष्ट" में बदल गया है, जो अभी भी सामान्यीकरण की अनुमति दे सकता है और केवल ऑफसेट की तुलना कर सकता है।) यह किसी भी हिस्से को सभी पॉइंटर्स मान रहा है। एक वस्तु हमेशा एक ही seg
मूल्य का उपयोग करें , कभी सामान्य नहीं। यह वह है जिसे आप "विशाल" मेमोरी मॉडल के विपरीत "बड़े" की आवश्यकता के लिए एक एबीआई की अपेक्षा करेंगे। ( टिप्पणियों में चर्चा देखें )।
(इस तरह के मेमोरी मॉडल में अधिकतम ऑब्जेक्ट का आकार 64kiB हो सकता है, उदाहरण के लिए, लेकिन बहुत अधिक अधिकतम कुल पता स्थान जिसमें ऐसी कई अधिकतम आकार की वस्तुओं के लिए जगह है। आईएसओ सी कार्यान्वयनों को ऑब्जेक्ट आकार पर सीमा की अनुमति देता है जो कि तुलना में कम है। अधिकतम मान (अहस्ताक्षरित) size_t
का प्रतिनिधित्व कर सकते हैं, SIZE_MAX
उदाहरण के लिए, यहां तक कि फ्लैट मेमोरी मॉडल सिस्टम पर भी, GNU C अधिकतम आकार की सीमा को आकार देता है PTRDIFF_MAX
ताकि गणना पर हस्ताक्षर किए गए अतिप्रवाह को अनदेखा किया जा सके।) इस उत्तर और टिप्पणियों में चर्चा देखें ।
यदि आप किसी खंड से बड़ी वस्तुओं को अनुमति देना चाहते हैं, तो आपको एक "विशाल" मेमोरी मॉडल की p++
आवश्यकता होती है, जिसमें किसी सरणी के माध्यम से लूप करते समय, या इंडेक्सिंग / पॉइंटर अंकगणित करते समय एक पॉइंटर के ऑफसेट भाग को ओवरफ्लो करने के बारे में चिंता करने की आवश्यकता होती है। यह हर जगह धीमी कोड की ओर जाता है, लेकिन शायद इसका मतलब यह p < q
होगा कि अलग-अलग ऑब्जेक्ट्स के लिए पॉइंटर्स के लिए काम करना होगा, क्योंकि एक "विशाल" मेमोरी मॉडल को लक्षित करने वाला कार्यान्वयन सामान्य रूप से सभी पॉइंटर्स को हर समय सामान्य रखने का चयन करेगा। देखें कि पास, दूर और विशाल संकेत क्या हैं? - x86 वास्तविक मोड के लिए कुछ वास्तविक सी कंपाइलरों के पास "विशाल" मॉडल के लिए संकलन करने का एक विकल्प था जहां सभी पॉइंटर्स "विशाल" तक डिफ़ॉल्ट रूप से घोषित किए गए जब तक कि अन्यथा घोषित न हो।
x86 रियल-मोड सेगमेंटेशन केवल गैर-फ्लैट मेमोरी मॉडल संभव नहीं है , यह केवल यह बताने के लिए एक उपयोगी ठोस उदाहरण है कि इसे C / C ++ कार्यान्वयन द्वारा कैसे नियंत्रित किया जाता है। वास्तविक जीवन में, कार्यान्वयन ने आईएसओ सी को far
बनाम near
पॉइंटर्स की अवधारणा के साथ विस्तारित किया , प्रोग्रामर को यह चुनने की अनुमति दी कि वे कुछ सामान्य डेटा सेगमेंट के सापेक्ष 16-बिट ऑफ़सेट भाग के आसपास बस स्टोरिंग / पासिंग से दूर हो सकते हैं।
लेकिन एक शुद्ध आईएसओ सी कार्यान्वयन के लिए एक छोटे मेमोरी मॉडल (16-बिट पॉइंटर्स के साथ समान 64kiB में कोड को छोड़कर) या बड़े या विशाल सभी पॉइंटर्स 32-बिट होने के बीच चुनना होगा। कुछ लूप केवल ऑफ़सेट भाग को बढ़ाकर अनुकूलित कर सकते हैं, लेकिन पॉइंटर ऑब्जेक्ट्स को छोटे होने के लिए अनुकूलित नहीं किया जा सकता है।
यदि आप जानते हैं कि किसी भी कार्यान्वयन के लिए जादू की हेरफेर क्या थी, तो आप इसे शुद्ध सी में लागू कर सकते हैं । समस्या यह है कि विभिन्न प्रणालियाँ अलग-अलग पते का उपयोग करती हैं और विवरण किसी भी पोर्टेबल मैक्रोज़ द्वारा परिचालित नहीं किए जाते हैं।
या शायद नहीं: इसमें एक विशेष सेगमेंट टेबल या कुछ से कुछ देखना शामिल हो सकता है, जैसे कि वास्तविक मोड के बजाय x86 संरक्षित मोड जहां पते का सेगमेंट हिस्सा एक इंडेक्स है, न कि छोड़ा जाने वाला मान। आप संरक्षित मोड में आंशिक रूप से ओवरलैपिंग सेगमेंट सेट कर सकते हैं, और पते के सेगमेंट चयनकर्ता भागों को आवश्यक रूप से संबंधित खंड आधार पते के समान क्रम में भी आदेश नहीं दिया जा सकता है। यदि कोई GDT और / या LDT आपकी प्रक्रिया में पठनीय पृष्ठों पर मैप नहीं किया गया है, तो एक xg से संरक्षित मोड में xg से संरक्षित मोड में पॉइंटर: एक पॉइंटर को प्राप्त करना सिस्टम कॉल को शामिल कर सकता है।
(निश्चित रूप से मुख्यधारा के OS के लिए x86 एक फ्लैट मेमोरी मॉडल का उपयोग करते हैं ताकि सेगमेंट बेस हमेशा 0 हो (थ्रेड-लोकल स्टोरेज का उपयोग करके fs
या gs
सेगमेंट को छोड़कर ), और केवल 32-बिट या 64-बिट "ऑफ़सेट" भाग को पॉइंटर के रूप में उपयोग किया जाता है ।)
आप मैन्युअल रूप से विभिन्न विशिष्ट प्लेटफार्मों के लिए कोड जोड़ सकते हैं, जैसे डिफ़ॉल्ट मान फ्लैट, या #ifdef
x86 वास्तविक मोड का पता लगाने के लिए कुछ और फिर uintptr_t
16-बिट हिस्सों में विभाजित करने के लिए seg -= off>>4; off &= 0xf;
उन हिस्सों को वापस 32-बिट संख्या में संयोजित करें।