size_t बनाम uintptr_t


246

सी मानक की गारंटी है कि size_tएक प्रकार है जो किसी भी सरणी सूचकांक पकड़ सकता है। इसका मतलब है कि, तार्किक रूप से, size_tकिसी भी सूचक प्रकार को धारण करने में सक्षम होना चाहिए। मैंने कुछ साइटों पर पढ़ा है जो मुझे गोगल्स पर मिली हैं कि यह कानूनी है और / या हमेशा काम करना चाहिए:

void *v = malloc(10);
size_t s = (size_t) v;

तो फिर C99 में, मानक ने प्रकार intptr_tऔर uintptr_tप्रकार पेश किए, जिन पर हस्ताक्षर किए गए और अहस्ताक्षरित प्रकार इंगित करने में सक्षम हैं:

uintptr_t p = (size_t) v;

तो उपयोग करने के बीच अंतर क्या है size_tऔर uintptr_t? दोनों अहस्ताक्षरित हैं, और दोनों को किसी भी सूचक प्रकार को रखने में सक्षम होना चाहिए, इसलिए वे कार्यात्मक रूप से समान लगते हैं। क्या स्पष्टता के अलावा, इसके बजाय uintptr_t(या बेहतर अभी तक void *) का उपयोग करने के लिए कोई वास्तविक सम्मोहक कारण है size_t? एक अपारदर्शी संरचना में, जहां क्षेत्र को केवल आंतरिक कार्यों द्वारा नियंत्रित किया जाएगा, क्या ऐसा करने का कोई कारण नहीं है?

एक ही टोकन द्वारा, ptrdiff_tएक हस्ताक्षरित प्रकार सूचक मतभेदों को रखने में सक्षम है, और इसलिए किसी भी सूचक को रखने में सक्षम है, इसलिए यह किस प्रकार से अलग है intptr_t?

इन सभी प्रकारों में मूल रूप से एक ही फ़ंक्शन के तुच्छ रूप से अलग-अलग संस्करण नहीं हैं? यदि नहीं, तो क्यों? मैं उनमें से एक के साथ क्या नहीं कर सकता जो मैं दूसरे के साथ नहीं कर सकता? यदि हां, तो C99 ने भाषा में दो अनिवार्य रूप से शानदार प्रकार क्यों जोड़े?

मैं फ़ंक्शन पॉइंटर्स की अवहेलना करने के लिए तैयार हूं, क्योंकि वे वर्तमान समस्या पर लागू नहीं होते हैं, लेकिन उनका उल्लेख करने के लिए स्वतंत्र महसूस करते हैं, क्योंकि मुझे एक संदेह है कि वे "सही" उत्तर के लिए केंद्रीय होंगे।

जवाबों:


236

size_tएक प्रकार है जो किसी भी सरणी सूचकांक को पकड़ सकता है। इसका मतलब है कि, तार्किक रूप से, size_t किसी भी सूचक प्रकार को धारण करने में सक्षम होना चाहिए

जरुरी नहीं! उदाहरण के लिए खंडित 16-बिट आर्किटेक्चर के दिनों में वापस जाएं: एक सरणी एक एकल खंड (इसलिए 16-बिट size_tकरना) तक सीमित हो सकती है लेकिन आपको कई खंड मिल सकते हैं (इसलिए 32-बिट intptr_tप्रकार को चुनने की आवश्यकता होगी खंड के साथ-साथ इसके भीतर ऑफसेट)। मुझे पता है कि ये बातें समान रूप से पता लगाने योग्य अस्वाभाविक आर्किटेक्चर के इन दिनों में अजीब लग रही हैं, लेकिन "2009 में सामान्य क्या है" की तुलना में एक व्यापक विविधता के लिए मानक एमईटी को पूरा करें, आप जानते हैं! -)


6
यह, कई अन्य लोगों, जो इसी निष्कर्ष पर कूद के साथ साथ, के बीच का अंतर बताते हैं size_tऔर uintptr_tलेकिन क्या बारे में ptrdiff_tऔर intptr_t- नहीं इन दोनों को लगभग किसी भी मंच पर मूल्यों की एक ही श्रेणी की दुकान करने में सक्षम होगा? क्यों हस्ताक्षरित और अहस्ताक्षरित सूचक आकार पूर्णांक प्रकार दोनों हैं, खासकर अगर ptrdiff_tपहले से ही हस्ताक्षरित सूचक आकार पूर्णांक प्रकार के उद्देश्य से कार्य करता है।
क्रिस लुत्ज़

8
मुख्य वाक्यांश " लगभग किसी भी मंच पर" है, @ क्रिस। एक कार्यान्वयन 0xf000-0xffff रेंज में पॉइंटर्स को प्रतिबंधित करने के लिए स्वतंत्र है - इसके लिए 16bit intptr_t की आवश्यकता है लेकिन केवल 12/13-बिट ptrdiff_t।
पैक्साडीब्लो

29
@ क्रिस, केवल उसी सरणी के अंदर पॉइंटर्स के लिए यह उनके अंतर को लेने के लिए अच्छी तरह से परिभाषित है। तो, ठीक उसी खंड पर 16-बिट आर्किटेक्चर (सरणी को एक सेगमेंट के अंदर रहना चाहिए, लेकिन दो अलग-अलग सरणियां अलग-अलग सेगमेंट में हो सकती हैं) पॉइंटर्स 4 बाइट्स होने चाहिए लेकिन सूचक अंतर 2 बाइट्स हो सकते हैं!
एलेक्स मार्टेली

6
@AlexMartelli: सिवाय इसके कि सूचक अंतर सकारात्मक या नकारात्मक हो सकते हैं। मानक के लिए size_tकम से कम 16 बिट्स होने की आवश्यकता होती है, लेकिन ptrdiff_tकम से कम 17 बिट्स होने का (जो व्यवहार में इसका मतलब है कि यह कम से कम 32 बिट्स होगा)।
कीथ थॉम्पसन

3
नेवरमाइंड ने आर्किटेक्चर को खंडित किया, x86-64 जैसी आधुनिक वास्तुकला के बारे में क्या? इस वास्तुकला के शुरुआती कार्यान्वयन आपको केवल 48-बिट पता योग्य स्थान देते हैं, लेकिन संकेत स्वयं 64-बिट डेटा प्रकार हैं। स्मृति का सबसे बड़ा सन्निहित ब्लॉक आप उचित रूप से पता कर सकते हैं 48-बिट, इसलिए मुझे कल्पना SIZE_MAXकरना चाहिए कि 2 ** 64 नहीं होना चाहिए। यह आपको संबोधित करते हुए सपाट संबोधन का उपयोग कर रहा है; SIZE_MAXडेटा पॉइंटर के बीच एक बेमेल होने के लिए कोई विभाजन आवश्यक नहीं है ।
एंडन एम। कोलमैन

89

आपके कथन के बारे में:

"सी मानक की गारंटी है कि size_tएक प्रकार है जो किसी भी सरणी सूचकांक को पकड़ सकता है। इसका मतलब है कि, तार्किक रूप से, size_tकिसी भी सूचक प्रकार को धारण करने में सक्षम होना चाहिए।"

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

पॉइंटर्स और एरे इंडेक्स एक ही चीज नहीं हैं । यह एक अनुरूप कार्यान्वयन की परिकल्पना करने के लिए काफी प्रशंसनीय है, जो एरेज़ को 65536 तत्वों तक सीमित करता है, लेकिन पॉइंटर्स को किसी भी मूल्य को बड़े पैमाने पर 128-बिट एड्रेस स्पेस में संबोधित करने की अनुमति देता है।

C99 बताता है कि एक size_tचर की ऊपरी सीमा को परिभाषित किया गया है SIZE_MAXऔर यह 65535 के रूप में कम हो सकता है (C11 TR3, 7.18.3, C11 में अपरिवर्तित देखें)। यदि वे आधुनिक प्रणालियों में इस सीमा तक सीमित थे, तो संकेत काफी सीमित होंगे।

व्यवहार में, आप शायद पाएंगे कि आपकी धारणा धारण करती है, लेकिन ऐसा इसलिए नहीं है क्योंकि मानक इसकी गारंटी देता है। क्योंकि यह वास्तव में इसकी गारंटी नहीं देता है।


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

सभी पिल्ले प्यारे हैं। यह बात प्यारी है। इसलिए यह बात एक पिल्ला होना चाहिए।

कठपुतली की काट-छाँट या अन्यथा का यहाँ कोई असर नहीं है, मैं यह कह रहा हूँ कि दो तथ्य निष्कर्ष पर नहीं जाते हैं, क्योंकि पहले दो वाक्य प्यारे चीजों की मौजूदगी की अनुमति देते हैं जो कि पिल्ले नहीं हैं ।

यह आपके पहले कथन के समान है, जरूरी नहीं कि दूसरा अनिवार्य हो।


एलेक्स मार्टेली के लिए टिप्पणियों में मैंने जो कहा, उसे फिर से लिखने के बजाय, मैं केवल स्पष्टीकरण के लिए धन्यवाद कहूंगा, लेकिन अपने प्रश्न के दूसरे भाग ( ptrdiff_tबनाम intptr_tभाग) को दोहराऊंगा ।
क्रिस लुत्ज़

5
@ इवान, अधिकांश संचार के साथ, कुछ बुनियादी वस्तुओं की साझा समझ होनी चाहिए। यदि आप इस उत्तर को "मज़ेदार मजाक" के रूप में देखते हैं, तो मैं आपको विश्वास दिलाता हूं कि यह मेरे इरादे की गलतफहमी है। यह मानते हुए कि आप मेरी 'तार्किक अड़चन' टिप्पणी का उल्लेख कर रहे हैं (मैं कोई अन्य संभावना नहीं देख सकता), इसका मतलब था कि एक तथ्यात्मक बयान के रूप में, न कि ओपी की कीमत पर किए गए कुछ बयान। यदि आप गलतफहमी की संभावना को कम करने के लिए कुछ ठोस सुधार का सुझाव देना चाहते हैं (केवल एक सामान्य शिकायत के बजाय), तो मुझे विचार करने में खुशी होगी।
paxdiablo

1
@ivan_pozdeev - वह संपादन का एक अप्रिय और कठोर जोड़ा है, और मुझे इस बात का कोई सबूत नहीं है कि paxdiablo किसी पर "मज़ाक उड़ा रहा था"। अगर मैं ओपी होता, तो मैं इस दाईं ओर रोल करता। ....
पूर्व निहिलो

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

1
@paxdiablo ठीक है, मुझे लगता है कि "यह वास्तव में एक पतन है" कम संरक्षण है।
ivan_pozdeev

36

मैं अन्य सभी उत्तरों को खंड सीमाओं, विदेशी वास्तुशिल्प और इसी तरह के तर्क के बारे में खुद के लिए खड़ा होने दूंगा।

क्या नाम में साधारण अंतर उचित कारण के लिए उचित प्रकार का उपयोग करने के लिए पर्याप्त कारण नहीं है?

यदि आप आकार का भंडारण कर रहे हैं, तो उपयोग करें size_t। यदि आप एक पॉइंटर स्टोर कर रहे हैं, तो उपयोग करें intptr_t। आपके कोड को पढ़ने वाला व्यक्ति तुरंत जान जाएगा कि "अहा, यह किसी चीज़ का आकार है, शायद बाइट्स में", और "ओह, यहां एक पॉइंटर मान को पूर्णांक के रूप में संग्रहीत किया जा रहा है, किसी कारण से"।

अन्यथा, आप बस unsigned long(या, इन आधुनिक समयों में unsigned long long) का उपयोग कर सकते हैं। आकार सब कुछ नहीं है, टाइप नाम का अर्थ है जो उपयोगी है क्योंकि यह कार्यक्रम का वर्णन करने में मदद करता है।


मैं सहमत हूं, लेकिन मैं एक हैक / ट्रिक के बारे में विचार कर रहा था (कि मैं स्पष्ट रूप से दस्तावेज़ करूँगा, निश्चित रूप से) एक size_tफ़ील्ड में एक पॉइंटर प्रकार को शामिल करना ।
क्रिस लुत्ज़

@MarkAdler मानक को पूर्णांक के रूप में सूचक को इंगित करने की आवश्यकता नहीं है: किसी भी सूचक प्रकार को पूर्णांक प्रकार में परिवर्तित किया जा सकता है। पहले से निर्दिष्ट के अलावा, परिणाम कार्यान्वयन-परिभाषित है। यदि परिणाम पूर्णांक प्रकार में प्रदर्शित नहीं किया जा सकता है, तो व्यवहार अपरिभाषित है। परिणाम किसी पूर्णांक प्रकार के मानों की श्रेणी में नहीं होना चाहिए। इस प्रकार, केवल void*, intptr_tऔर uintptr_tडेटा के लिए किसी भी सूचक का प्रतिनिधित्व करने में सक्षम होने की गारंटी है।
एंड्रयू Svietlichnyy

12

यह संभव है कि सबसे बड़े सरणी का आकार एक संकेतक से छोटा हो। खंडित आर्किटेक्चर के बारे में सोचें - पॉइंटर्स 32-बिट हो सकते हैं, लेकिन एक एकल खंड केवल 64KB (उदाहरण के लिए पुराने वास्तविक-मोड 8086 आर्किटेक्चर) को संबोधित करने में सक्षम हो सकता है।

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


लेकिन आप एरियर्स की तरह ही पॉइंटर्स इंडेक्स कर सकते हैं, इसलिए इसे size_tभी हैंडल करना चाहिए ? या कुछ दूर के खंड में गतिशील सरणियाँ अभी भी अपने सेगमेंट में अनुक्रमण तक सीमित रहेंगी?
क्रिस लुत्ज

इंडेक्सिंग पॉइंटर्स केवल तकनीकी रूप से उन सरणी के आकार के लिए समर्थित होते हैं जो वे इंगित करते हैं - इसलिए यदि कोई सरणी 64KB आकार तक सीमित है, तो यह सब सूचक अंकगणित का समर्थन करने की आवश्यकता है। हालाँकि, MS-DOS कंपाइलरों ने एक 'विशाल' मेमोरी मॉडल का समर्थन किया था, जहाँ दूर के पॉइंटर्स (32-बिट सेगमेंट पॉइंटर्स) में हेरफेर किया गया था, ताकि वे पूरे मेमोरी को एक ही सरणी के रूप में संबोधित कर सकें - लेकिन दृश्यों के पीछे इंगित करने के लिए किया गया आरती बहुत बदसूरत - जब ऑफसेट 16 (या कुछ) के मान को बढ़ाता है, तो ऑफसेट को 0 पर लपेटा गया था और खंड का हिस्सा बढ़ गया था।
माइकल बूर

7
En.wikipedia.org/wiki/C_memory_model#Memory_segmentation पढ़ें और MS-DOS प्रोग्रामर के लिए रोएं जो मर गए ताकि हम आजाद हो सकें।
जस्टिक

इससे भी बुरी बात यह थी कि stdlib फ़ंक्शन विशाल कीवर्ड की देखभाल नहीं करता था। सभी के लिए 16bit एमएस-सी strकार्य करता है और बोर्लेन्ड के लिए भी memकार्य ( memset, memcpy, memmove)। इसका मतलब है कि जब आप ओवरफ्लो हो जाते हैं, तो आप मेमोरी के एक हिस्से को ओवरराइट कर सकते हैं, जो कि हमारे एम्बेडेड प्लेटफॉर्म पर डिबग करने के लिए मजेदार था।
पैट्रिक श्ल्टर

@ जूलरी: 8086 खंडों वाली वास्तुकला को सी में अच्छी तरह से समर्थित नहीं किया गया है, लेकिन मैं किसी अन्य वास्तुकला के बारे में नहीं जानता हूं जो उन मामलों में अधिक कुशल है जहां 1 एमबी पता स्थान पर्याप्त है लेकिन 64K एक नहीं होगा। कुछ आधुनिक JVM वास्तव में x86 वास्तविक मोड की तरह बहुत अधिक संबोधित करने का उपयोग करते हैं, एक 32GB पता स्थान में ऑब्जेक्ट आधार पते उत्पन्न करने के लिए 32 बिट ऑब्जेक्ट संदर्भों को स्थानांतरित करने के लिए 3 बिट्स का उपयोग करते हैं।
सुपरकैट

5

मैं कल्पना करता हूं (और यह सभी प्रकार के नामों के लिए जाता है) कि यह कोड में आपके इरादों को बेहतर तरीके से बताता है।

उदाहरण के लिए, भले ही unsigned shortऔर wchar_tविंडोज पर एक ही आकार के हैं (मुझे लगता है), wchar_tइसके बजाय unsigned shortइरादे को दर्शाता है कि आप इसका उपयोग केवल कुछ मनमानी संख्या के बजाय एक विस्तृत चरित्र को संग्रहीत करने के लिए करेंगे।


लेकिन यहाँ एक अंतर है - मेरे सिस्टम पर, एक के लिए एक का उपयोग करने की wchar_tतुलना में बहुत बड़ा है एक unsigned shortदूसरे के लिए गलत होगा और एक गंभीर (और आधुनिक) पोर्टेबिलिटी चिंता पैदा करेगा, जबकि पोर्टेबिलिटी के बीच चिंताओं size_tऔर uintptr_tदूर-दूर की जमीन में झूठ लगता है 1980 की-कुछ (तारीख पर अंधेरे में बेतरतीब छुरा),
क्रिस लुत्ज़

ट्च! लेकिन फिर से, size_tऔर uintptr_tअभी भी उनके नाम में निहित उपयोग हैं।
ड्रीमलैक्स

वे करते हैं, और मैं जानना चाहता था कि क्या केवल स्पष्टता से परे इसके लिए कोई प्रेरणा थी। और यह पता चला है कि वहाँ है।
क्रिस लुत्ज़

3

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

तो यकीन है, जिस तरह से चीजें बस गईं, हमें अब तक इतने प्रकारों की जरूरत नहीं है।

लेकिन LP64 में, बल्कि एक सामान्य प्रतिमान के रूप में, हमें सिस्टम कॉल इंटरफ़ेस के लिए size_t और ssize_t की आवश्यकता थी। एक और अधिक विवश विरासत या भविष्य की प्रणाली की कल्पना कर सकते हैं, जहां पूर्ण 64-बिट प्रकार का उपयोग करना महंगा है और वे I / O को 4GB से अधिक बड़ा करना चाहते हैं, लेकिन अभी भी 64-बिट पॉइंटर्स हैं।

मुझे लगता है कि आपको आश्चर्य करना होगा: जो विकसित हो सकता है, भविष्य में क्या हो सकता है। (शायद 128-बिट वितरित-सिस्टम इंटरनेट-वाइड पॉइंटर्स, लेकिन सिस्टम कॉल में 64 बिट्स से अधिक नहीं, या शायद एक "विरासत" 32-बिट सीमा भी है। :-) छवि कि विरासत प्रणालियों को नया सी कंपाइलर मिल सकता है .. ।

इसके अलावा, यह देखिए कि आसपास क्या मौजूद था। Zillion 286 वास्तविक-मोड मेमोरी मॉडल के अलावा, सीडीसी 60-बिट शब्द / 18-बिट पॉइंटर मेनफ्रेम के बारे में कैसे? क्रे श्रृंखला के बारे में कैसे? कभी भी सामान्य मान ILP64, LP64, LLP64। (मुझे हमेशा लगा कि Microsoft LLP64 के साथ ढोंग कर रहा था, यह P64 होना चाहिए था।) मैं निश्चित रूप से सभी ठिकानों को कवर करने की कोशिश करने वाली समिति की कल्पना कर सकता हूं ...


-9
int main(){
  int a[4]={0,1,5,3};
  int a0 = a[0];
  int a1 = *(a+1);
  int a2 = *(2+a);
  int a3 = 3[a];
  return a2;
}

यह कहते हुए कि intptr_t को हमेशा size_t और वीज़ा वर्सा के लिए स्थानापन्न करना चाहिए।


10
यह सभी शो सी। का एक विशेष सिंटैक्स क्विकर है। एरे इंडेक्सिंग को x [y] के संदर्भ में * (x + y) के बराबर परिभाषित किया गया है, और क्योंकि + 3 और 3 + प्रकार और मूल्य में समान हैं, आप कर सकते हैं 3 का उपयोग करें [a] या [3]।
फ्रेड नर्क
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.