यदि मेमोरी एड्रेस नहीं है तो वास्तव में C पॉइंटर क्या है?


206

C के बारे में एक सम्मानित स्रोत में, &ऑपरेटर से चर्चा करने के बाद निम्नलिखित जानकारी दी गई है :

... यह थोड़ा दुर्भाग्यपूर्ण है कि शब्दावली [का पता] बनी हुई है, क्योंकि यह उन लोगों को भ्रमित करता है जो यह नहीं जानते हैं कि पते क्या हैं, और उन लोगों को गुमराह करते हैं जो ऐसा करते हैं: संकेत के बारे में ऐसा सोचना जैसे कि वे पते आमतौर पर दुःख की ओर ले जाते हैं .. ।

अन्य सामग्री जो मैंने पढ़ी है (समान रूप से सम्मानित स्रोतों से, मैं कहूंगा) हमेशा अनैतिक रूप से पॉइंटर्स और &ऑपरेटर को मेमोरी एड्रेस के रूप में संदर्भित किया जाता है । मैं इस मामले की वास्तविकता को खोजना जारी रखना पसंद करूंगा, लेकिन जब प्रतिष्ठित स्रोत KIND OF असहमत होते हैं तो यह कठिन होता है।

अब मैं थोड़ा उलझन में हूँ - क्या वास्तव में एक सूचक है, फिर, यदि स्मृति पता नहीं है?

पुनश्च

लेखक बाद में कहता है: ... मैं 'का पता' का उपयोग करना जारी रखूंगा, हालांकि, क्योंकि एक अलग शब्द [शब्द] का आविष्कार करना और भी बुरा होगा।


118
पॉइंटर एक वैरिएबल है जो एक एड्रेस रखता है । इसका अपना पता भी है । यह एक सूचक और एक सरणी के बीच मूलभूत अंतर है। एक सरणी प्रभावी रूप से एक पता है (और निहितार्थ से, इसका पता स्वयं है )।
WhozCraig

7
बोली के लिए आपका "सम्मानित स्रोत" क्या है?
कॉर्नस्टालक्स

22
अंतिम प्रतिष्ठित स्रोत भाषा का मानक है न कि इससे प्राप्त अर्द्ध-व्युत्पन्न और लेखक की अर्द्ध-खींची गई पुस्तकें। मैंने इसे कठिन तरीके से सीखा, लगभग हर गलती जो मैं कर सकता था और धीरे-धीरे सी के एक मानसिक मॉडल का निर्माण कर रहा था जो मानक द्वारा वर्णित कुछ के करीब था और फिर अंत में मानक के मॉडल के साथ उक्त मॉडल की जगह ले रहा था।
एलेक्सी फ्रांज़

9
@ लोगों को पॉइंटर = पूर्णांक लगता है क्योंकि यह अक्सर ऐसा होता है (x86 लिनक्स और विंडोज "हमें सिखाते हैं"), क्योंकि लोग सामान्यीकरण से प्यार करते हैं, क्योंकि लोग भाषा के मानक को अच्छी तरह से नहीं जानते हैं और क्योंकि उनके पास मौलिक रूप से अलग अनुभव नहीं है प्लेटफार्मों। उन लोगों को यह मानने की संभावना है कि डेटा के लिए एक पॉइंटर और फ़ंक्शन के लिए एक पॉइंटर को एक दूसरे में परिवर्तित किया जा सकता है और डेटा को कोड के रूप में निष्पादित किया जा सकता है और कोड को डेटा के रूप में एक्सेस किया जा सकता है। हालांकि यह वॉन न्यूमन आर्किटेक्चर (1 एड्रेस स्पेस के साथ) पर सही हो सकता है, लेकिन जरूरी नहीं कि हार्वर्ड आर्किटेक्चर (w / कोड और डेटा स्पेस) पर सही हो।
एलेक्सी फ्रांज़

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

जवाबों:


148

सी मानक परिभाषित नहीं करता है कि एक सूचक आंतरिक रूप से क्या है और यह आंतरिक रूप से कैसे काम करता है। यह जानबूझकर है ताकि प्लेटफार्मों की संख्या को सीमित न किया जाए, जहां सी को संकलित या व्याख्या की गई भाषा के रूप में लागू किया जा सकता है।

एक पॉइंटर मान किसी तरह का आईडी या हैंडल या कई आईडी का संयोजन हो सकता है (जैसे कि x86 सेगमेंट और ऑफ़सेट के लिए हैलो) और जरूरी नहीं कि एक वास्तविक मेमोरी एड्रेस हो। यह आईडी कुछ भी हो सकती है, यहां तक ​​कि एक निश्चित आकार का टेक्स्ट स्ट्रिंग भी। गैर-पता निरूपण C दुभाषिया के लिए विशेष रूप से उपयोगी हो सकता है।


34
समझाने के लिए बहुत कुछ नहीं है। स्मृति में हर चर का अपना पता होता है। लेकिन आपको उनके पते उनके पास इंगित करने की आवश्यकता नहीं है। इसके बजाय आप अपने वेरिएबल को 1 से जो भी नंबर दें और उस नंबर को पॉइंटर में स्टोर करें। यह भाषा मानक के अनुसार पूरी तरह से कानूनी है जब तक कि कार्यान्वयन जानता है कि उन नंबरों को पते में कैसे बदलना है और उन संख्याओं के साथ सूचक अंकगणित कैसे करना है और मानक द्वारा आवश्यक अन्य सभी चीजें हैं।
एलेक्सी फ्रांज़

4
मैं जोड़ना चाहूंगा कि x86 पर, एक मेमोरी एड्रेस में एक सेगमेंट सेलेक्टर और एक ऑफसेट होता है, इसलिए एक पॉइंटर को सेगमेंट के रूप में दर्शाया जाता है: ऑफ़सेट अभी भी मेमोरी एड्रेस का उपयोग कर रहा है।
थान

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

8
@ लुंडिन आपको क्रांतिकारी या वैज्ञानिक होने की आवश्यकता नहीं है। मान लीजिए कि आप भौतिक 16-बिट मशीन पर 32-बिट मशीन का अनुकरण करना चाहते हैं और आप डिस्क स्टोरेज का उपयोग करके अपने 64KB RAM को 4GB तक बढ़ाते हैं और 32-बिट पॉइंटर्स को एक विशाल फ़ाइल में ऑफ़सेट के रूप में लागू करते हैं। वे संकेत वास्तविक स्मृति पते नहीं हैं।
एलेक्सी फ्रुंज़

6
मैंने अब तक जो सबसे अच्छा उदाहरण देखा है, वह सिम्बॉलिक्स लिस्प मशीन (लगभग 1990) का सी कार्यान्वयन था। प्रत्येक C ऑब्जेक्ट को एक लिस्प सरणी के रूप में लागू किया गया था, और पॉइंटर्स को एक सरणी और एक इंडेक्स की जोड़ी के रूप में लागू किया गया था। लिस्प की सरणी सीमा की जाँच के कारण, आप कभी भी एक वस्तु से दूसरी वस्तु पर नहीं जा सकते हैं।
बामर

62

मुझे आपके स्रोत के बारे में निश्चित नहीं है, लेकिन आप जिस भाषा का वर्णन कर रहे हैं वह सी मानक से आता है:

६.५.३.२ पते और अप्रत्यक्ष परिचालकों
[...]
३. अनुपयोगी और परिचालक अपने ऑपरेंड के पते की पैदावार करते हैं। [...]

तो ... हाँ, संकेत स्मृति पते को इंगित करते हैं। कम से कम यह है कि सी मानक यह कैसे मतलब है पता चलता है।

इसे और अधिक स्पष्ट रूप से कहने के लिए, एक पॉइंटर एक चर है जो किसी पते का मूल्य रखता है । किसी ऑब्जेक्ट का पता (जो एक पॉइंटर में संग्रहीत किया जा सकता है) को अपर ऑपरेटर के साथ लौटाया जाता है ।&

मैं "42 वालेबी वे, सिडनी" पते को एक चर में संग्रहीत कर सकता हूं (और यह चर एक प्रकार का "सूचक" होगा, लेकिन चूंकि यह एक स्मृति पता नहीं है, इसलिए यह ऐसा कुछ नहीं है जिसे हम ठीक से "सूचक" कहेंगे)। आपके कंप्यूटर में मेमोरी के बकेट के लिए पते हैं। पॉइंटर्स एक पते का मान संग्रहीत करते हैं (अर्थात एक पॉइंटर "42 वालेबाई वे, सिडनी" का मूल्य रखता है, जो एक पता है)।

संपादित करें: मैं एलेक्सी फ्रंज की टिप्पणी पर विस्तार करना चाहता हूं।

क्या वास्तव में एक सूचक है? आइए सी मानक को देखें:

6.2.5 प्रकार
[...]
20. [...]
एक सूचक प्रकार एक फ़ंक्शन प्रकार या ऑब्जेक्ट प्रकार से प्राप्त किया जा सकता है, जिसे संदर्भित प्रकार कहा जाता है । एक पॉइंटर प्रकार किसी ऑब्जेक्ट का वर्णन करता है जिसका मान संदर्भित प्रकार की इकाई को एक संदर्भ प्रदान करता है। संदर्भित प्रकार T से प्राप्त एक पॉइंटर प्रकार को कभी-कभी 'पॉइंटर टू टी' कहा जाता है। संदर्भित प्रकार से एक पॉइंटर प्रकार के निर्माण को '' पॉइंटर टाइप व्युत्पत्ति '' कहा जाता है। एक सूचक प्रकार एक पूर्ण वस्तु प्रकार है।

अनिवार्य रूप से, पॉइंटर्स एक मान संग्रहीत करते हैं जो किसी वस्तु या फ़ंक्शन का संदर्भ प्रदान करता है। एक प्रकार का। पॉइंटर्स का उद्देश्य एक वैल्यू को स्टोर करना है जो किसी ऑब्जेक्ट या फ़ंक्शन को एक संदर्भ प्रदान करता है, लेकिन यह हमेशा ऐसा नहीं होता है:

6.3.2.3 पॉइंटर्स
[...]
5. एक पूर्णांक को किसी भी पॉइंटर प्रकार में परिवर्तित किया जा सकता है। पहले से निर्दिष्ट के अलावा, परिणाम कार्यान्वयन-परिभाषित है, सही ढंग से गठबंधन नहीं किया जा सकता है, संदर्भित प्रकार की इकाई को इंगित नहीं कर सकता है, और एक जाल प्रतिनिधित्व हो सकता है।

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

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

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

यह खत्म होने की तुलना में मैंने सोचा था कि यह होगा ...


3
एक सी दुभाषिया में, एक सूचक एक गैर-पता आईडी / संभाल / आदि पकड़ सकता है।
एलेक्सी फ्रुंज़े

4
@exebook मानक किसी भी तरह से संकलित सी तक सीमित नहीं है
एलेक्सी

7
@ लुंडिन ब्रावो! आइए मानक को अधिक अनदेखा करें! जैसे कि हमने इसे पहले से ही अनदेखा नहीं किया है और इसकी वजह से छोटी गाड़ी और खराब पोर्टेबल सॉफ्टवेयर का उत्पादन नहीं किया है। इसके अलावा, कृपया यह नहीं कि मूल प्रश्न सामान्य है और इस तरह के एक सामान्य उत्तर की आवश्यकता है।
अलेक्सी फ्रुंज़े

3
जब अन्य लोग कह रहे हैं कि एक पॉइंटर एक हैंडल हो सकता है या एक पते के अलावा कुछ और हो सकता है, तो उनका मतलब केवल यह नहीं है कि आप एक पॉइंटर को एक पॉइंटर में डालकर एक पॉइंटर में डेटा जमा कर सकते हैं। उनका मतलब है कि कंपाइलर पॉइंटर्स को लागू करने के लिए मेमोरी एड्रेस के अलावा कुछ और इस्तेमाल कर रहे होंगे। DEC के ABI के साथ अल्फा प्रोसेसर पर, एक फ़ंक्शन पॉइंटर फ़ंक्शन का पता नहीं था, लेकिन किसी फ़ंक्शन के डिस्क्रिप्टर का पता था, और डिस्क्रिप्टर में फ़ंक्शन का पता और फ़ंक्शन मापदंडों के बारे में कुछ डेटा शामिल थे। मुद्दा यह है कि सी मानक बहुत लचीला है।
एरिक पोस्टपिसिल

5
@ लुंडिन: संकेत जो वास्तविक दुनिया में मौजूदा कंप्यूटर सिस्टम के 100% पर पूर्णांक पते के रूप में कार्यान्वित किए जाते हैं, गलत है। कंप्यूटर शब्द पते और खंड-ऑफसेट पते के साथ मौजूद हैं। कंपाइलर अभी भी निकट और दूर के बिंदुओं के समर्थन के साथ मौजूद हैं। PDP-11 कंप्यूटर मौजूद हैं, RSX-11 और टास्क बिल्डर और इसके ओवरले के साथ, जिसमें एक पॉइंटर को डिस्क से फ़ंक्शन को लोड करने के लिए आवश्यक जानकारी की पहचान करनी चाहिए। यदि कोई ऑब्जेक्ट मेमोरी में नहीं है, तो एक पॉइंटर किसी ऑब्जेक्ट का मेमोरी एड्रेस नहीं हो सकता है!
एरिक पोस्टपिसिल

39

सूचक बनाम चर

इस तस्वीर में,

pointer_p एक पॉइंटर है जो 0x12345 पर स्थित है, और 0x1234567 पर एक वेरिएबल वेरिएबल_v की ओर इशारा कर रहा है।


16
यह न केवल सूचक के विपरीत पते की धारणा को संबोधित करता है, बल्कि यह इस बिंदु पर एकीकृत रूप से याद करता है कि एक पता केवल एक पूर्णांक नहीं है।
गिल्स एसओ- बुराई को रोकना '

19
-1, यह सिर्फ बताता है कि एक पॉइंटर क्या है। यह सवाल नहीं था - और आप उन सभी जटिलताओं को परे धकेल रहे हैं जिनके बारे में सवाल है
एलेक्सिस

34

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

एक पॉइंटर एक पते की तरह होता है जिसमें यह इंगित करता है कि किसी ऑब्जेक्ट को कहां खोजना है। इस सादृश्य की एक तात्कालिक सीमा यह है कि सभी बिंदुओं में वास्तव में एक पता नहीं होता है। NULLएक सूचक है जो एक पता नहीं है। पॉइंटर चर की सामग्री वास्तव में तीन प्रकारों में से एक हो सकती है:

  • किसी वस्तु का पता , जिसे निस्तारित किया जा सकता है (यदि pउस पते का पता होता है, xतो अभिव्यक्ति *pका मान समान है x);
  • एक अशक्त सूचक , जिसमें NULLसे एक उदाहरण है;
  • अमान्य सामग्री, जो किसी ऑब्जेक्ट को इंगित नहीं करती है (यदि pकोई मान्य मान नहीं रखता है, तो *pकुछ भी कर सकता है ("अपरिभाषित व्यवहार"), प्रोग्राम को काफी सामान्य संभावना को क्रैश करने के साथ)।

इसके अलावा, यह कहना अधिक सटीक होगा कि एक पॉइंटर (यदि वैध और गैर-शून्य) में एक पता है: एक पॉइंटर इंगित करता है कि किसी ऑब्जेक्ट को कहां ढूंढना है, लेकिन इससे जुड़ी अधिक जानकारी है।

विशेष रूप से, एक पॉइंटर में एक प्रकार होता है। अधिकांश प्लेटफार्मों पर, सूचक के प्रकार का रनटाइम पर कोई प्रभाव नहीं होता है, लेकिन इसका एक प्रभाव होता है जो संकलन के समय टाइप से परे होता है। यदि pएक पॉइंटर टू int( int *p;) है, तो p + 1एक पूर्णांक को इंगित करता है जो sizeof(int)बाइट्स के बाद है p(यह मानते हुए p + 1कि अभी भी एक वैध पॉइंटर है)। यदि उस बिंदु के समान ( ) को इंगित करने के qलिए एक संकेतक है , तो उसी पते के रूप में नहीं है । यदि आप पॉइंटर को पते के रूप में सोचते हैं, तो यह बहुत सहज नहीं है कि "अगला पता" अलग-अलग पॉइंटर्स के लिए एक ही स्थान पर अलग है।charpchar *q = p;q + 1p + 1

कुछ वातावरणों में विभिन्न अभ्यावेदन (मेमोरी में अलग-अलग बिट पैटर्न) के साथ कई पॉइंटर मान होना संभव है जो स्मृति में एक ही स्थान पर इंगित करते हैं। आप इन्हें अलग-अलग पॉइंटर्स के रूप में एक ही पते पर, या एक ही स्थान के लिए अलग-अलग पते के रूप में सोच सकते हैं - इस मामले में रूपक स्पष्ट नहीं है। ==ऑपरेटर हमेशा आपको बतलाता है कि दो ऑपरेंड इसलिए इन वातावरण आप रख सकते हैं, एक ही स्थान पर ओर इशारा करते हैं p == q, भले ही pऔर qअलग सा पैटर्न है।

यहां तक ​​कि ऐसे वातावरण भी हैं जहां संकेत पते से परे अन्य जानकारी को ले जाते हैं, जैसे कि टाइप या अनुमति जानकारी। आप इनका सामना किए बिना आसानी से एक प्रोग्रामर के रूप में अपने जीवन के माध्यम से जा सकते हैं।

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

सब के सब, पते के रूप में संकेत की सोच भी बुरा नहीं है जब तक आप ध्यान रखें कि

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

दूसरे रास्ते पर जाना कहीं ज्यादा तकलीफदेह है। सब कुछ जो एक पते की तरह दिखता है, एक सूचक हो सकता है । कहीं गहरे किसी सूचक को एक बिट पैटर्न के रूप में दर्शाया जाता है जिसे पूर्णांक के रूप में पढ़ा जा सकता है, और आप कह सकते हैं कि यह पूर्णांक एक पता है। लेकिन दूसरे तरीके से जाना, प्रत्येक पूर्णांक एक सूचक नहीं है।

पहले कुछ प्रसिद्ध सीमाएं हैं; उदाहरण के लिए, एक पूर्णांक जो आपके प्रोग्राम के पते के स्थान के बाहर का स्थान बताता है, एक मान्य सूचक नहीं हो सकता है। एक गलत पता एक डेटा प्रकार के लिए एक वैध सूचक नहीं बनाता है जिसे संरेखण की आवश्यकता होती है; उदाहरण के लिए, एक मंच पर जहां int4-बाइट संरेखण की आवश्यकता होती है, 0x7654321 एक वैध int*मूल्य नहीं हो सकता है ।

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

unsigned int x = 0;
unsigned short *p = (unsigned short*)&x;
p[0] = 1;
printf("%u = %u\n", x, *p);

आप उम्मीद कर सकते हैं कि एक रन-ऑफ-द-मिल मशीन पर जहां sizeof(int)==4और sizeof(short)==2, यह या तो प्रिंट करता है 1 = 1?(छोटा-एंडियन) या 65536 = 1?(बड़ा-एंडियन)। लेकिन GCC 4.4 के साथ मेरे 64-बिट लिनक्स पीसी पर:

$ c99 -O2 -Wall a.c && ./a.out 
a.c: In function main’:
a.c:6: warning: dereferencing pointer p does break strict-aliasing rules
a.c:5: note: initialized from here
0 = 1?

जीसीसी हमें यह बताने के लिए पर्याप्त है कि इस सरल उदाहरण में क्या गलत है - अधिक जटिल उदाहरणों में, कंपाइलर नोटिस नहीं कर सकता है। चूंकि pएक अलग प्रकार है &x, इसलिए कुछ बिंदुओं को (कुछ अच्छी तरह से परिभाषित अपवादों के बाहर) pकिन &xबिंदुओं को प्रभावित नहीं किया जा सकता है , इसे बदलना । इसलिए संकलक xएक रजिस्टर में मूल्य रखने के लिए स्वतंत्रता में है और इस रजिस्टर को *pपरिवर्तनों के रूप में अपडेट नहीं करता है। कार्यक्रम एक ही पते पर दो बिंदुओं को डीरफेर करता है और दो अलग-अलग मान प्राप्त करता है!

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

इसलिए पॉइंटर्स को अपनी समझ में पहला कदम के रूप में संबोधित करें, लेकिन उस अंतर्ज्ञान का बहुत दूर तक पालन न करें।


5
+1। अन्य जवाबों से लगता है कि एक पॉइंटर टाइप जानकारी के साथ आता है। यह पते / आईडी / जो भी चर्चा से कहीं अधिक महत्वपूर्ण है।
21

+1 प्रकार की जानकारी के बारे में उत्कृष्ट बिंदु। मुझे यकीन नहीं है कि संकलक उदाहरण सही थे ... यह बहुत संभावना नहीं है, उदाहरण के लिए, यह *p = 3सफल होने की गारंटी है जब पी को आरंभीकृत नहीं किया गया है।
लार्श

@ लार्स यू आर राइट, थैंक्स, मैंने ऐसा कैसे लिखा? मैंने इसे एक उदाहरण से बदल दिया जो मेरे पीसी पर आश्चर्यजनक व्यवहार को प्रदर्शित करता है।
गिल्स एसओ- बुराई को रोकना '

1
उम, NULL है ((शून्य *) 0) ..?
अनिकेत इंग

1
@ gnasher729 नल पॉइंटर है एक सूचक। NULLनहीं है, लेकिन यहाँ आवश्यक विवरण के स्तर के लिए, यह एक अप्रासंगिक व्याकुलता है। यहां तक ​​कि दिन-प्रतिदिन की प्रोग्रामिंग के लिए, इस तथ्य को NULLलागू किया जा सकता है जो ऐसा कुछ कहता है जो "पॉइंटर" नहीं कहता है, अक्सर नहीं आता है (मुख्य रूप NULLसे एक वैरिएड फ़ंक्शन में गुजर रहा है - लेकिन वहां भी, अगर आप इसे कास्टिंग नहीं कर रहे हैं , आप पहले से ही यह धारणा बना रहे हैं कि सभी सूचक प्रकारों का समान प्रतिनिधित्व है)।
गिल्स एसओ- बुराई को रोकें '

19

एक पॉइंटर एक चर है जो HOLDS मेमोरी एड्रेस है, न कि एड्रेस ही। हालांकि, आप एक पॉइंटर को रोक सकते हैं - और मेमोरी स्थान तक पहुंच प्राप्त कर सकते हैं।

उदाहरण के लिए:

int q = 10; /*say q is at address 0x10203040*/
int *p = &q; /*means let p contain the address of q, which is 0x10203040*/
*p = 20; /*set whatever is at the address pointed by "p" as 20*/

बस। यह इत्ना आसान है।

यहां छवि विवरण दर्ज करें

एक कार्यक्रम जो मैं कह रहा हूं उसे प्रदर्शित करने के लिए और इसका आउटपुट यहां है:

http://ideone.com/rcSUsb

कार्यक्रम:

#include <stdio.h>

int main(int argc, char *argv[])
{
  /* POINTER AS AN ADDRESS */
  int q = 10;
  int *p = &q;

  printf("address of q is %p\n", (void *)&q);
  printf("p contains %p\n", (void *)p);

  p = NULL;
  printf("NULL p now contains %p\n", (void *)p);
  return 0;
}

5
यह और भी अधिक भ्रमित कर सकता है। ऐलिस, क्या आप एक बिल्ली देख सकते हैं? नहीं, मैं केवल एक बिल्ली की मुस्कान देख सकता हूं। तो यह कहना कि पॉइंटर एक एड्रेस है या पॉइंटर एक वैरिएबल है जो एक एड्रेस रखता है या यह कहता है कि पॉइंटर एक कॉन्सेप्ट का नाम है जो एड्रेस के आइडिया को संदर्भित करता है, बुक राइटर कितनी दूर भ्रामक नीवीबीज में जा सकते हैं?
5

@exebook उन सीज़न में जो पॉइंटर्स में हैं, यह काफी सरल है। शायद कोई चित्र मदद करेगा?
अनिकेत इंग

5
एक पॉइंटर आवश्यक रूप से एक पता नहीं रखता है। C दुभाषिया में, यह कुछ और हो सकता है, किसी प्रकार की ID / संभाल।
एलेक्सी फ्रांज़

"लेबल" या चर नाम एक संकलक / कोडांतरक है और मशीन के स्तर पर मौजूद नहीं है, इसलिए मुझे नहीं लगता कि इसे मेमोरी में दिखाई देना चाहिए।
बेन

1
@Aniket एक पॉइंटर वैरिएबल में पॉइंटर मान हो सकता है। आपको केवल fopenएक चर में परिणाम को संग्रहीत करने की आवश्यकता है यदि आपको इसे एक से अधिक बार उपयोग करने की आवश्यकता है (जो, इसके लिए fopen, हर समय बहुत अधिक है)।
गिलेस एसओ- बुराई को रोकना '

16

यह बताना मुश्किल है कि उन पुस्तकों के लेखकों का वास्तव में क्या मतलब है। सूचक में कोई पता होता है या नहीं, इस बात पर निर्भर करता है कि आप किसी पते को कैसे परिभाषित करते हैं और आप एक पॉइंटर को कैसे परिभाषित करते हैं।

जो सभी उत्तर लिखे गए हैं, उन्हें देखते हुए, कुछ लोग मानते हैं कि (1) एक पते का पूर्णांक होना चाहिए और (2) एक सूचक को विनिर्देश में ऐसा नहीं होने के आभासी द्वारा होने की आवश्यकता नहीं है। इन मान्यताओं के साथ, फिर स्पष्ट रूप से इंगितकर्ताओं में पते नहीं होते हैं।

हालाँकि, हम देखते हैं कि जबकि (2) शायद सच है, (1) शायद सच होना जरूरी नहीं है। और इस तथ्य से क्या लेना-देना है कि & ऑपरेटर के पते को @ CornStalks के उत्तर के अनुसार कहा जाता है ? क्या इसका मतलब यह है कि विनिर्देश के लेखक एक सूचक को एक पते के लिए रखने का इरादा रखते हैं?

तो क्या हम कह सकते हैं, सूचक में एक पता होता है, लेकिन एक पते का पूर्णांक होना जरूरी नहीं है? शायद।

मुझे लगता है कि यह सब जिबरिश पांडित्यपूर्ण शब्दार्थ है। यह पूरी तरह से बेकार है व्यावहारिक रूप से बोलना। क्या आप एक संकलक के बारे में सोच सकते हैं जो कोड को इस तरह से उत्पन्न करता है कि एक पॉइंटर का मूल्य पता नहीं है? यदि ऐसा है तो क्या? बिल्कुल यही मैने सोचा...

मुझे लगता है कि पुस्तक का लेखक (पहला अंश जो दावा करता है कि संकेत केवल पते नहीं हैं) संभवतः इस बात का जिक्र है कि एक सूचक इसके साथ निहित प्रकार की जानकारी रखता है।

उदाहरण के लिए,

 int x;
 int* y = &x;
 char* z = &x;

y और z दोनों संकेत हैं, लेकिन y + 1 और z + 1 भिन्न हैं। यदि वे स्मृति पते हैं, तो क्या वे भाव आपको समान मूल्य नहीं देंगे?

और यहाँ संकेत के बारे में सोच के रूप में अगर वे पते थे आमतौर पर दु: ख होता है । कीड़े इसलिए लिखे गए हैं क्योंकि लोग संकेत के बारे में सोचते हैं जैसे कि वे पते थे , और यह आमतौर पर दु: ख की ओर जाता है

55555 संभवतः एक पॉइंटर नहीं है, हालांकि यह एक पता हो सकता है, लेकिन (int *) 55555 एक पॉइंटर है। 55555 + 1 = 55556, लेकिन (int *) 55555 + 1 55559 (+/- साइज़ोफ़ (int) के संदर्भ में अंतर है)।


1
+1 सूचक अंकगणित को इंगित करने के लिए पते पर अंकगणित के समान नहीं है।
kutschkem

16-बिट 8086 के मामले में, एक मेमोरी एड्रेस एक खंड आधार + ऑफसेट द्वारा वर्णित है, दोनों 16 बिट्स। खंड आधार + ऑफसेट के कई संयोजन हैं जो स्मृति में समान पता देते हैं। यह farसूचक केवल "पूर्णांक" नहीं है।
वॉनब्रांड

@vonbrand मुझे समझ नहीं आता कि आपने वह टिप्पणी क्यों पोस्ट की है। उस मुद्दे पर अन्य उत्तरों के तहत टिप्पणियों के रूप में चर्चा की गई है। बस हर दूसरे उत्तर के बारे में माना जाता है कि पता = पूर्णांक और पूर्णांक नहीं है। मैं बस इसे इंगित करता हूं और ध्यान देता हूं कि यह सही हो सकता है या नहीं। जवाब में मेरा पूरा कहना यह है कि यह प्रासंगिक नहीं है। यह सब सिर्फ पांडित्य है, और मुख्य मुद्दे को अन्य उत्तरों में संबोधित नहीं किया जा रहा है।
शुक्रिया

@tang, "सूचक == पता" का विचार गलत है । हर कोई और उनकी पसंदीदा चाची यह कहती रहती है कि यह सही नहीं है।
वॉनब्रांड

@vonbrand, और आपने मेरी पोस्ट के तहत वह टिप्पणी क्यों की? मैंने यह नहीं कहा कि यह सही है या गलत है। वास्तव में, यह कुछ परिदृश्यों / मान्यताओं में सही है, लेकिन हमेशा नहीं। मुझे फिर से पोस्ट के बिंदु (दूसरी बार) के बारे में संक्षेप में बताऊं। जवाब में मेरा पूरा कहना यह है कि यह प्रासंगिक नहीं है। यह सब सिर्फ पांडित्य है, और मुख्य मुद्दे को अन्य उत्तरों में संबोधित नहीं किया जा रहा है। उन उत्तरों पर टिप्पणी करना अधिक उचित होगा जो दावा करते हैं कि सूचक == पता या पता == पूर्णांक है। खंड के संबंध में एलेक्सी की पोस्ट के तहत मेरी टिप्पणी देखें: ऑफसेट।
१६:१३ की

15

खैर, एक सूचक एक अमूर्त है एक स्मृति स्थान का प्रतिनिधित्व करने । ध्यान दें कि उद्धरण यह नहीं कहता है कि पॉइंटर्स के बारे में सोचना जैसे कि वे मेमोरी एड्रेस गलत थे, यह सिर्फ यह कहता है कि यह "आमतौर पर दुःख की ओर जाता है"। दूसरे शब्दों में, यह आपको गलत अपेक्षाओं की ओर ले जाता है।

दुःख का सबसे संभावित स्रोत निश्चित रूप से सूचक अंकगणित है, जो वास्तव में सी की ताकत में से एक है। यदि कोई संकेतक एक पता था, तो आप उम्मीद करेंगे कि अंकगणित पता अंकगणित हो; लेकिन ऐसा नहीं है। उदाहरण के लिए, एक पते में 10 जोड़ने से आपको एक पता देना चाहिए जो 10 पते इकाइयों से बड़ा है; लेकिन एक पॉइंटर में 10 जोड़ने से यह उस वस्तु के आकार के 10 गुना बढ़ जाता है जो इसे इंगित करता है (और वास्तविक आकार भी नहीं है, लेकिन एक संरेखण सीमा तक गोल है)। एक साथ int *32-बिट पूर्णांकों के साथ एक साधारण वास्तुकला पर, 10 को जोड़ने के लिए यह 40 को संबोधित इकाइयों (बाइट) द्वारा यह भी वृद्धि होगी। अनुभवी सी प्रोग्रामर इसके बारे में जानते हैं और इसके साथ रहते हैं, लेकिन आपका लेखक स्पष्ट रूप से मैला रूपकों का प्रशंसक नहीं है।

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


12

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

उदाहरण के लिए, दिया गया:

union {
    int i;
    char c;
} u;

आपके तीन अलग-अलग पॉइंटर्स हो सकते हैं जो सभी को एक ही ऑब्जेक्ट की ओर इशारा करते हैं:

void *v = &u;
int *i = &u.i;
char *c = &u.c;

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

v==i && i==c

हालाँकि, यदि आप प्रत्येक पॉइंटर को बढ़ाते हैं, तो आप देखेंगे कि जिस प्रकार वे इंगित करते हैं वह प्रासंगिक हो जाता है।

i++;
c++;
// You can't perform arithmetic on a void pointer, so no v++
i != c

इस बिंदु पर चर iऔर cमान अलग-अलग होंगे, क्योंकि अगले-सुलभ पूर्णांक के पते को शामिल करने का i++कारण बनता iहै, और अगले-पता योग्य चरित्र को इंगित करने का c++कारण बनता cहै। आमतौर पर, पूर्णांक वर्णों की तुलना में अधिक मेमोरी लेते हैं, इसलिए दोनों iबड़े होने के cबाद बड़े मूल्य के साथ समाप्त हो जाएंगे ।


2
+1 धन्यवाद। संकेत के साथ, मूल्य और प्रकार अविभाज्य हैं क्योंकि मनुष्य अपनी आत्मा से शरीर को अलग कर सकता है।
अकी सुहाइकोनें

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

8

मार्क बेसे ने पहले ही कहा था, लेकिन इसे समझने तक फिर से जोर देने की जरूरत है।

पॉइंटर का शाब्दिक 3 की तुलना में एक चर के साथ बहुत कुछ करना है।

सूचक है (एक पते की) एक मूल्य के एक टपल और एक प्रकार (जैसे कि केवल पढ़ने के रूप में अतिरिक्त गुण, के साथ)। प्रकार (और यदि कोई अतिरिक्त पैरामीटर) संदर्भ को आगे परिभाषित या प्रतिबंधित कर सकता है; जैसे। __far ptr, __near ptr: पते का संदर्भ क्या है: ढेर, ढेर, रैखिक पता, कहीं से ऑफसेट, भौतिक स्मृति या क्या।

यह प्रकार की संपत्ति है जो सूचक अंकगणित को पूर्णांक अंकगणित से थोड़ा अलग बनाता है।

चर नहीं होने के सूचक के काउंटर उदाहरणों को अनदेखा करने के लिए बहुत अधिक हैं

  • fopen एक FILE पॉइंटर लौटाता है। (जहां चर रहा है)

  • स्टैक पॉइंटर या फ़्रेम पॉइंटर आमतौर पर अनजाने रजिस्टर होते हैं

    *(int *)0x1231330 = 13; - एक पॉइंटर_ऑफ_इन्टेगर प्रकार के मनमाने ढंग से पूर्णांक मान को कास्टिंग करना और एक चर को प्रस्तुत किए बिना पूर्णांक लिखना / पढ़ना।

सी-प्रोग्राम के जीवनकाल में अस्थायी पॉइंटर्स के कई अन्य उदाहरण होंगे जिनके पते नहीं हैं - और इसलिए वे चर नहीं हैं, लेकिन संकलित समय से जुड़े प्रकार के साथ भाव / मान हैं।


8

आप सही हैं और समझदार हैं। आम तौर पर, एक सूचक केवल एक पता होता है, इसलिए आप इसे पूर्णांक में डाल सकते हैं और किसी भी अंकगणित कर सकते हैं।

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

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


पीसी ... फ्लैट मेमोरी मॉडल? चयनकर्ता क्या हैं?
थान

Riight। और जब अगला आर्किटेक्चर परिवर्तन चारों ओर आता है, तो शायद अलग-अलग कोड adn डेटा स्पेस के साथ, या कोई वापस जाने योग्य सेगमेंट आर्किटेक्चर के लिए जाता है (जो सुरक्षा के लिए बहुत मायने रखता है, शायद अनुमतियों की जांच करने के लिए सेगमेंट नंबर + ऑफसेट में कुछ कुंजी जोड़ सकता है) सुंदर "संकेत बस पूर्णांक हैं" नीचे दुर्घटनाग्रस्त हो जाता है।
वॉनब्रांड

7

सी में किसी भी अन्य चर की तरह एक सूचक, मूल रूप से बिट्स का एक संग्रह है जिसे एक या एक से अधिक unsigned charसंक्षिप्त मूल्यों द्वारा दर्शाया जा सकता है (जैसा कि किसी अन्य प्रकार के कार्बल के साथ, मूल्यों sizeof(some_variable)की संख्या का संकेत देगा unsigned char)। एक पॉइंटर को अन्य वेरिएबल्स से अलग बनाता है वह यह है कि सी कंपाइलर पॉइंटर में बिट्स की पहचान करते हुए, किसी तरह, एक ऐसी जगह की व्याख्या करेगा जहां एक वेरिएबल को स्टोर किया जा सकता है। सी में, कुछ अन्य भाषाओं के विपरीत, कई चर के लिए स्थान का अनुरोध करना संभव है, और फिर उस सेट में किसी भी मान को एक पॉइंटर में किसी भी अन्य चर के लिए एक पॉइंटर को परिवर्तित करना संभव है।

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

ध्यान दें कि जब पॉइंटर्स कुछ सार होते हैं, तो वे काफी सार नहीं होते हैं कि एक कचरा कलेक्टर को लागू करने के लिए पूरी तरह से मानकों के अनुरूप सी कंपाइलर की अनुमति दें। सी कंपाइलर निर्दिष्ट करता है कि संकेत सहित हर चर को unsigned charमूल्यों के अनुक्रम के रूप में दर्शाया गया है । किसी भी चर को देखते हुए, कोई इसे संख्याओं के अनुक्रम में विघटित कर सकता है और बाद में संख्याओं के उस क्रम को मूल प्रकार के चर में बदल सकता है। नतीजतन, यह एक कार्यक्रम के लिए संभव होगाcalloc कुछ स्टोरेज (इसे पॉइंटर प्राप्त करना) के , वहां कुछ स्टोर करें, पॉइंटर को बाइट्स की श्रृंखला में विघटित करें, स्क्रीन पर प्रदर्शित करें, और सभी को मिटा दें उनके संदर्भ में। यदि प्रोग्राम ने फिर कीबोर्ड से कुछ नंबरों को स्वीकार कर लिया, तो उन लोगों को एक पॉइंटर पर पुनर्गठित किया, और फिर उस पॉइंटर से डेटा पढ़ने की कोशिश की, और यदि उपयोगकर्ता ने उन्हीं नंबरों को दर्ज किया जो प्रोग्राम ने पहले प्रदर्शित किया था, तो प्रोग्राम को डेटा आउटपुट करने की आवश्यकता होगी उस में संग्रहीत किया गया थाcalloc'स्मृति। चूंकि कोई अनुमान योग्य तरीका नहीं है, इसलिए कंप्यूटर को पता चल सकता है कि क्या उपयोगकर्ता ने प्रदर्शित किए गए नंबरों की एक प्रति बनाई है, कोई भी अनुमान योग्य नहीं होगा कंप्यूटर को पता चल सकता है कि क्या भविष्य में उपरोक्त मेमोरी कभी भी एक्सेस की जा सकती है।


महान ओवरहेड पर, शायद आप पॉइंटर मूल्य के किसी भी उपयोग का पता लगा सकते हैं जो इसके संख्यात्मक मूल्य को "लीक" कर सकता है, और आवंटन को पिन कर सकता है ताकि कचरा कलेक्टर इसे इकट्ठा या स्थानांतरित नहीं करेगा (जब तक freeकि इसे स्पष्ट रूप से नहीं कहा जाता है, निश्चित रूप से)। क्या परिणामी क्रियान्वयन वह सब कुछ उपयोगी होगा जो एक और मामला है, क्योंकि इकट्ठा करने की क्षमता बहुत सीमित हो सकती है, लेकिन आप कम से कम इसे कचरा कलेक्टर कह सकते हैं: पॉइंटर असाइनमेंट और अंकगणित मूल्य को "लीक" नहीं करेंगे, लेकिन char*अज्ञात मूल के किसी भी प्रवेश की जाँच करनी होगी।
स्टीव जेसोप

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

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

@SteveJessop: GC प्रणाली के लिए उपयोगी होने के लिए, यह या तो मज़बूती से उस मेमोरी को रिलीज़ करने में सक्षम होना चाहिए जिस पर freeकॉल नहीं किया गया है, या किसी मुक्त वस्तु के किसी भी संदर्भ को लाइव ऑब्जेक्ट के संदर्भ में बनने से रोका जा सकता है [संसाधनों का उपयोग करते समय भी] स्पष्ट आजीवन प्रबंधन, जीसी अभी भी उपयोगी रूप से बाद के कार्य कर सकता है]; एक जीसी प्रणाली जो कभी-कभी वस्तुओं को गलत तरीके से संबंध बनाती है क्योंकि उनके लिए लाइव संदर्भ होने पर उपयोग योग्य हो सकता है यदि एन वस्तुओं की संभावना को एक साथ पिन किया जाए, क्योंकि एन बड़े हो जाता है । जब तक कोई संकलक त्रुटि को ध्वजांकित करने के लिए तैयार नहीं है ...
सुपरकैट

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

6

एक सूचक एक चर प्रकार है जो मूल रूप से C / C ++ में उपलब्ध है और इसमें एक मेमोरी एड्रेस होता है। किसी भी अन्य चर की तरह इसका स्वयं का एक पता है और मेमोरी लेता है (राशि प्लेटफ़ॉर्म विशिष्ट है)।

एक समस्या जिसे आप देखेंगे कि भ्रम के परिणामस्वरूप एक फ़ंक्शन के भीतर संदर्भ को केवल सूचक द्वारा पास करके बदलने की कोशिश की जा रही है। यह फ़ंक्शन स्कोप पर पॉइंटर की एक प्रतिलिपि बना देगा और जहाँ यह नया पॉइंटर "पॉइंट्स" है, फ़ंक्शन को संदर्भित करने वाले स्कोप पर पॉइंटर के संदर्भ को नहीं बदलेगा। फ़ंक्शन के भीतर वास्तविक पॉइंटर को संशोधित करने के लिए आमतौर पर एक पॉइंटर को पॉइंटर पास किया जाएगा।


1
आम तौर पर, यह एक हैंडल / आईडी है। आमतौर पर, यह एक सादा पता है।
एलेक्सी फ्रुंज़

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

6

संक्षिप्त सारांश (जिसे मैं शीर्ष पर भी रखूंगा):

(0) पाइंट के बारे में सोचना पतों के रूप में अक्सर एक अच्छा शिक्षण उपकरण है, और अक्सर साधारण डेटा प्रकारों के लिए पॉइंटर्स के लिए वास्तविक कार्यान्वयन है।

(1) लेकिन कई पर, शायद सबसे अधिक, फ़ंक्शन के लिए संकलक पते पते नहीं हैं, लेकिन एक पते (आमतौर पर 2x, कभी-कभी अधिक) से बड़े हैं, या वास्तव में स्मृति में एक संरचना की ओर संकेत करते हैं, जिसमें फ़ंक्शन और सामान जैसे पते शामिल हैं एक निरंतर पूल।

(2) डेटा सदस्यों को इंगित करने और विधियों के लिए संकेत अक्सर अजनबी भी होते हैं।

(3) FAR और NEAR पॉइंटर मुद्दों के साथ लिगेसी x86 कोड

(4) कई उदाहरण, विशेष रूप से आईबीएम एएस / 400, सुरक्षित "फैट पॉइंटर्स" के साथ।

मुझे यकीन है कि आप अधिक पा सकते हैं।

विवरण:

UMMPPHHH !!!!! अब तक के कई उत्तर काफी विशिष्ट "प्रोग्रामर वेनी" उत्तर हैं - लेकिन कंपाइलर वेनी या हार्डवेयर वेनी नहीं। चूंकि मैं एक हार्डवेयर वेनी होने का दिखावा करता हूं, और अक्सर कंपाइलर वेनीज़ के साथ काम करता हूं, मुझे अपने दो सेंट में फेंक दें:

कई पर, शायद सबसे, सी कंपाइलर, प्रकार के डेटा के लिए एक सूचक T, वास्तव में, का पता है T

ठीक।

लेकिन, इनमें से कई कंपाइलरों पर, कुछ पॉइंटर्स एड्रेस नहीं हैं। आप इसे देखकर बता सकते हैं sizeof(ThePointer)

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

हममें से कई लोगों के पास पुरानी इंटेल x86 खंडित वास्तुकला की दर्दनाक यादें हैं, जिनमें NEAR POINTERs और FAR POINTERS हैं। शुक्र है कि ये अब तक लगभग विलुप्त हो चुके हैं, इसलिए केवल एक त्वरित सारांश: 16 बिट वास्तविक मोड में, वास्तविक रैखिक पता था

LinearAddress = SegmentRegister[SegNum].base << 4 + Offset

जबकि संरक्षित मोड में, यह हो सकता है

LinearAddress = SegmentRegister[SegNum].base + offset

परिणामी पते को सेगमेंट में निर्धारित सीमा के विरुद्ध जांचा जा रहा है। कुछ कार्यक्रमों में वास्तव में मानक C / C ++ FAR और NEAR पॉइंटर घोषणाओं का उपयोग नहीं किया गया था, लेकिन कई लोगों ने कहा *T--- लेकिन कंपाइलर और लिंकर स्विच थे इसलिए, उदाहरण के लिए, कोड पॉइंटर्स पॉइंटर्स के पास हो सकते हैं, जो कुछ भी है उसके खिलाफ सिर्फ 32 बिट ऑफसेट सीएस (कोड सेगमेंट) रजिस्टर, जबकि डेटा पॉइंटर्स एफएआर पॉइंटर्स हो सकते हैं, जो कि 16 बिट खंड संख्या और 48 बिट मूल्य के लिए 32 बिट ऑफसेट निर्दिष्ट करते हैं। अब, ये दोनों मात्राएँ निश्चित रूप से पते से संबंधित हैं, लेकिन चूंकि वे समान आकार नहीं हैं, इसलिए उनमें से कौन सा पता है? इसके अलावा, सेगमेंट ने वास्तविक पते से संबंधित सामानों के अलावा केवल पठन-पाठन, पठन-पाठन, निष्पादन योग्य - की भी अनुमति दी।

एक और दिलचस्प उदाहरण, IMHO, (या, शायद, आईबीएम एएस / 400 परिवार था)। यह कंप्यूटर C ++ में OS को लागू करने वाले पहले में से एक था। इस मशीन पर पॉइंटर्स आम तौर पर वास्तविक पते के आकार के 2X थे - जैसेइस प्रस्तुति यह प्रस्तुतिकहते हैं, 128 बिट पॉइंटर्स, लेकिन वास्तविक पते 48-64 बिट्स थे, और, फिर से, कुछ अतिरिक्त जानकारी, जिसे क्षमता कहा जाता है, जिसने रीड, राइट और साथ ही बफर ओवरफ्लो को रोकने के लिए एक सीमा प्रदान की। हां: आप इसे C / C ++ के साथ कम्पेटिबल तरीके से कर सकते हैं - और अगर यह सर्वव्यापी था, तो चीनी PLA और स्लाव माफिया इतने सारे पश्चिमी कंप्यूटर सिस्टम में हैकिंग नहीं करेंगे। लेकिन ऐतिहासिक रूप से अधिकांश C / C ++ प्रोग्रामिंग ने प्रदर्शन के लिए सुरक्षा की उपेक्षा की है। सबसे दिलचस्प बात यह है कि AS400 परिवार ने ऑपरेटिंग सिस्टम को सुरक्षित पॉइंटर्स बनाने की अनुमति दी, जो कि अनवील किए गए कोड को दिया जा सकता है, लेकिन जो अनवील कोड को फोर्ज या छेड़छाड़ नहीं कर सकता है। फिर से, सुरक्षा, और मानकों का अनुपालन करते समय, बहुत ही मैला-कुचैला गैर-मानक कंप्लेंट C / C ++ कोड इतनी सुरक्षित प्रणाली में काम नहीं करेगा। फिर, आधिकारिक मानक हैं,

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

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

जैसा कि आप शायद मेरी (पर) सुरक्षा में मेरे अनंतिम से अनुमान लगा सकते हैं, मैं C / C ++ हार्डवेयर / सॉफ्टवेयर परियोजनाओं में शामिल रहा हूं जहां एक कच्चे पते की तुलना में एक पॉइंटर को क्षमता की तरह व्यवहार किया गया था।

मैं आगे बढ़ सकता था, लेकिन मुझे उम्मीद है कि आपको यह विचार मिलेगा।

संक्षिप्त सारांश (जिसे मैं शीर्ष पर भी रखूंगा):

(0) पाइंट के रूप में पाइंटर्स के बारे में सोचना अक्सर एक अच्छा शिक्षण उपकरण होता है, और अक्सर साधारण डेटा प्रकारों के लिए पाइंटर्स के लिए वास्तविक कार्यान्वयन होता है।

(1) लेकिन कई पर, शायद सबसे अधिक, फ़ंक्शन के लिए संकलक पते पते नहीं हैं, लेकिन एक पते (आमतौर पर 2X, कभी-कभी अधिक) से बड़े होते हैं, या वास्तव में स्मृति में एक संरचना की ओर इशारा करते हैं, जिसमें फ़ंक्शन और सामान जैसे पते शामिल होते हैं एक निरंतर पूल।

(2) डेटा सदस्यों को इंगित करने और विधियों के लिए संकेत अक्सर अजनबी भी होते हैं।

(3) FAR और NEAR पॉइंटर मुद्दों के साथ लिगेसी x86 कोड

(4) कई उदाहरण, विशेष रूप से आईबीएम एएस / 400, सुरक्षित "फैट पॉइंटर्स" के साथ।

मुझे यकीन है कि आप अधिक पा सकते हैं।


16 बिट वास्तविक मोड में LinearAddress = SegmentRegister.Selector * 16 + Offset(नोट 16 बार, 16 से शिफ्ट नहीं)। संरक्षित मोड में LinearAddress = SegmentRegister.base + offset(किसी भी प्रकार का कोई गुणन नहीं; खंड आधार को GDT / LDT में संग्रहीत किया जाता है और खंड के रजिस्टर में कैश किया गया है )।
एलेक्सी फ्रुंज़े

आप सेगमेंट बेस के बारे में भी सही हैं। मैंने गलत समझा था। यह सेगमेंट की सीमा है जो वैकल्पिक रूप से 4K द्वारा एकाधिक है। जब वह एक सेगमेंट रजिस्टर में मेमोरी से एक सेगमेंट डिस्क्रिप्टर को लोड करता है, तो उसे सेगमेंट बेस को हार्डवेयर द्वारा अनसक्रम्बल करने की आवश्यकता होती है।
क्रेजी गेलव

4

एक पॉइंटर सिर्फ एक और वैरिएबल है जिसका उपयोग मेमोरी लोकेशन (आमतौर पर दूसरे वेरिएबल के मेमोरी एड्रेस) के पते को रखने के लिए किया जाता है।


तो, सूचक वास्तव में एक स्मृति पता है? आप लेखक से असहमत हैं? बस समझने की कोशिश कर रहा हूं।
d0rmLife

सूचक का प्राथमिक कार्य किसी चीज़ की ओर इशारा करना है। वास्तव में यह कैसे हासिल किया जाता है और क्या कोई वास्तविक पता है या नहीं, परिभाषित नहीं है। एक पॉइंटर सिर्फ एक आईडी / हैंडल हो सकता है, वास्तविक पता नहीं।
एलेक्सी फ्रुंज़े

4

आप इसे इस तरह देख सकते हैं। पॉइंटर एक ऐसा मान है जो पता योग्य मेमोरी स्पेस में एक पते का प्रतिनिधित्व करता है।


2
एक पॉइंटर के लिए आवश्यक नहीं है कि वह वास्तविक मेमोरी एड्रेस को पकड़े। इसके अंतर्गत मेरा उत्तर और टिप्पणी देखें।
एलेक्सी फ्रांज़

क्या .... स्टैक पर पहले चर के लिए सूचक 0. प्रिंट नहीं करता है यह स्टैक फ्रेम के शीर्ष (या नीचे) को प्रिंट करता है कि यह कैसे लागू किया जाता है।
थान

@ थंग पहले चर के लिए ऊपर और नीचे समान हैं। और स्टैक के इस मामले में ऊपर या नीचे का पता क्या है?
वैलेंटाइन रादू

@ValentinRadu, आप इसे क्यों नहीं आज़मा रहे हैं .. जाहिर है आपने इसे आज़माया नहीं है।
थान

2
@ थान्ग यू आर राइट, मैंने कुछ बुरा अनुमान लगाया, मेरी रक्षा के लिए यहां सुबह 5 बजे है।
वैलेंटाइन रादू

3

एक पॉइंटर सिर्फ एक और वेरिएबल होता है जिसमें मेमोरी एड्रेस आमतौर पर दूसरे वेरिएबल का हो सकता है। एक पॉइंटर होने के कारण यह एक मेमोरी एड्रेस भी है।


1
जरूरी नहीं कि एक पता हो। Btw, क्या आपने अपना उत्तर पोस्ट करने से पहले मौजूदा उत्तर और टिप्पणियाँ पढ़ी हैं?
एलेक्सी फ्रांज़

3

एसी पॉइंटर एक मेमोरी पते के समान है, लेकिन मशीन पर निर्भर विवरणों के साथ-साथ सारगर्भित और साथ ही निचले स्तर के निर्देश सेट में कुछ सुविधाएँ नहीं मिली हैं।

उदाहरण के लिए, एक सी पॉइंटर अपेक्षाकृत समृद्ध प्रकार का होता है। यदि आप संरचनाओं की एक सरणी के माध्यम से एक संकेतक बढ़ाते हैं, तो यह अच्छी तरह से एक संरचना से दूसरे में कूदता है।

संकेत रूपांतरण नियमों के अधीन हैं और संकलन समय प्रकार की जाँच प्रदान करते हैं।

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

एक संकेतक को बूलियन वैरिएबल के रूप में इस्तेमाल किया जा सकता है: यह सच है कि यदि यह अशक्त है, और झूठा है तो यह सही है।

मशीन की भाषा में, यदि शून्य सूचक 0xFFFFFFFFFF जैसा मज़ेदार पता है, तो आपको उस मान के लिए स्पष्ट परीक्षण करने पड़ सकते हैं। C आपसे छुपाता है। भले ही अशक्त सूचक 0xFFFFFFFFFF है, तो आप इसका उपयोग करके परीक्षण कर सकते हैं if (ptr != 0) { /* not null! */}

पॉइंटर्स के उपयोग जो टाइप सिस्टम को अविकसित करते हैं अपरिभाषित व्यवहार का नेतृत्व करते हैं, जबकि मशीन भाषा में समान कोड को अच्छी तरह से परिभाषित किया जा सकता है। असेंबलर्स आपके द्वारा लिखे गए निर्देशों को इकट्ठा करेंगे, लेकिन सी कंपाइलर इस धारणा के आधार पर अनुकूलन करेंगे कि आपने कुछ भी गलत नहीं किया है। यदि एक float *pपॉइंटर एक पॉइंटर को इंगित करता है long n, और *p = 0.0निष्पादित किया जाता है, तो संकलक को इसे संभालने की आवश्यकता नहीं है। बाद के उपयोग के nलिए फ्लोट मूल्य के बिट पैटर्न को पढ़ना आवश्यक नहीं होगा, लेकिन शायद, यह एक अनुकूलित पहुंच होगी जो "सख्त अलियासिंग" धारणा पर आधारित है जिसे nछुआ नहीं गया है! यही है, यह धारणा कि कार्यक्रम अच्छी तरह से व्यवहार किया गया है, और इसलिए pइसे इंगित नहीं किया जाना चाहिए n

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

तो आप देख सकते हैं, मशीन के पते और सी पॉइंटर्स के बीच कई अर्थ अंतर हैं।


NULL संकेतकर्ता उस तरह से काम नहीं करते जैसा आपको लगता है कि वे सभी प्लेटफार्मों पर करते हैं - कृपया ऊपर सिस्कोपफोन पर मेरा उत्तर देखें। NULL == 0 एक धारणा है जो केवल x86 आधारित प्लेटफ़ॉर्म पर रहती है। कन्वेंशन का कहना है कि नए प्लेटफार्मों को x86 से मेल खाना चाहिए, हालांकि विशेष रूप से एम्बेडेड दुनिया में ऐसा नहीं है। संपादित करें: इसके अलावा, C हार्डवेयर से एक पॉइंटर तरीके के मूल्य को अमूर्त करने के लिए कुछ भी नहीं करता है - "ptr! = 0" एक प्लेटफ़ॉर्म पर NULL टेस्ट के रूप में काम नहीं करेगा जहाँ NULL! = 0.
DX-MON

1
डीएक्स-मॉन, जो मानक सी के लिए पूरी तरह से गलत है। NULL को 0 होने के लिए तैयार किया गया है, और उन्हें बयानों में परस्पर उपयोग किया जा सकता है। हार्डवेयर में NULL पॉइंटर प्रतिनिधित्व नहीं है या नहीं, सभी 0 बिट्स अप्रासंगिक हैं कि यह स्रोत कोड में कैसे प्रस्तुत किया जाता है।
मार्क बेसी

@ DX-MON मुझे डर है कि आप सही तथ्यों के साथ काम नहीं कर रहे हैं। सी में, एक अभिन्न निरंतर अभिव्यक्ति एक शून्य सूचक स्थिरांक के रूप में कार्य करती है, भले ही अशक्त सूचक शून्य पता हो। यदि आप एक सी संकलक के बारे में जानते हैं जहां ptr != 0कोई अशक्त परीक्षण नहीं है, तो कृपया इसकी पहचान बताएं (लेकिन ऐसा करने से पहले, विक्रेता को एक बग रिपोर्ट भेजें)।
काज

मैं देखता हूं कि आप क्या कर रहे हैं, लेकिन अशक्त बिंदुओं के बारे में आपकी टिप्पणी असंगत है क्योंकि आप बिंदुओं और स्मृति पतों को भ्रमित कर रहे हैं - वास्तव में प्रश्न में उद्धृत उद्धरण से बचने की सलाह देता है! सही कथन: C शून्य सूचक को शून्य होने के लिए परिभाषित करता है, भले ही ऑफसेट शून्य पर एक स्मृति पता कानूनी हो या नहीं।
एलेक्सिस

1
@alexis अध्याय और कविता, कृपया। C शून्य सूचक को शून्य होने के लिए परिभाषित नहीं करता है। C शून्य सूचक को निरूपित करने के लिए एक वाक्यविन्यास के रूप में शून्य (या किसी भी अभिन्न स्थिर अभिव्यक्ति जिसका मूल्य शून्य है) को परिभाषित करता है । faqs.org/faqs/C-faq/faq (धारा 5)।
काज

3

संकेत समझने से पहले हमें वस्तुओं को समझने की आवश्यकता है। ऑब्जेक्ट ऐसी इकाइयाँ हैं जो मौजूद होती हैं और एक स्थान निर्दिष्ट होता है जिसे एक पता कहा जाता है। एक पॉइंटर किसी भी अन्य वैरिएबल की तरह एक वैरिएबल है Cजिसमें एक प्रकार होता है, pointerजिसकी सामग्री को किसी ऑब्जेक्ट के पते के रूप में व्याख्या की जाती है जो निम्नलिखित ऑपरेशन का समर्थन करता है।

+ : A variable of type integer (usually called offset) can be added to yield a new pointer
- : A variable of type integer (usually called offset) can be subtracted to yield a new pointer
  : A variable of type pointer can be subtracted to yield an integer (usually called offset)
* : De-referencing. Retrieve the value of the variable (called address) and map to the object the address refers to.
++: It's just `+= 1`
--: It's just `-= 1`

एक सूचक को उस वस्तु के प्रकार के आधार पर वर्गीकृत किया जाता है जिसे वह वर्तमान में संदर्भित कर रहा है। जानकारी का एकमात्र हिस्सा वस्तु का आकार है।

कोई भी ऑब्जेक्ट एक ऑपरेशन &(पते) का समर्थन करता है , जो ऑब्जेक्ट के स्थान निर्दिष्ट (पते) को एक पॉइंटर ऑब्जेक्ट प्रकार के रूप में पुनर्प्राप्त करता है। यह नामकरण के आस-पास के भ्रम को समाप्त करना चाहिए क्योंकि यह &एक संकेतक के बजाय किसी ऑब्जेक्ट के संचालन के रूप में कॉल करने के लिए समझ में आता है जिसका परिणामी प्रकार ऑब्जेक्ट प्रकार का सूचक है।

नोट इस स्पष्टीकरण के दौरान, मैंने स्मृति की अवधारणा को छोड़ दिया है।


मुझे एक सामान्य प्रणाली में एक सामान्य सूचक के अमूर्त वास्तविकता पर आपका स्पष्टीकरण पसंद है। लेकिन, शायद स्मृति की चर्चा करना मददगार होगा। वास्तव में, अपने लिए बोलना, मुझे पता है कि यह होगा ...! मुझे लगता है कि बड़ी तस्वीर को समझने के लिए कनेक्शन पर चर्चा करना बहुत मददगार हो सकता है। +1 कोई भी :)
d0rmLife

@ d0rmLife: आपके पास अन्य उत्तरों में पर्याप्त स्पष्टीकरण है जो बड़ी तस्वीर को कवर करता है। मैं सिर्फ एक अन्य दृष्टिकोण के रूप में एक गणितीय सार स्पष्टीकरण देना चाहता था। इसके अलावा, IMHO, यह &'का पता' के रूप में कॉल करने में कम भ्रम पैदा करेगा, क्योंकि यह सूचक के बजाय किसी वस्तु से अधिक बंधा हुआ है '
अभिजीत

कोई अपराध नहीं है, लेकिन मैं अपने लिए तय करूंगा कि पर्याप्त व्याख्या क्या है। डेटा संरचना और मेमोरी आवंटन को पूरी तरह से समझाने के लिए एक पाठ्यपुस्तक पर्याप्त नहीं है। ;) .... वैसे भी, आपका जवाब अभी भी उपयोगी है , भले ही वह उपन्यास न हो।
d0rmLife

यह स्मृति की अवधारणा के बिना संकेत को संभालने के लिए कोई मतलब नहीं है । यदि ऑब्जेक्ट मेमोरी के बिना मौजूद है, तो यह एक जगह पर होना चाहिए, जहां कोई पता नहीं है - जैसे रजिस्टरों में। स्मृति का उपयोग करने के लिए 'और' का उपयोग करने में सक्षम होने के लिए।
अकी सुहिकोनेन

3

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

तकनीकी रूप से, एक पता सी में एक मूल्य नहीं है, क्योंकि (आईएसओ) सी में "मूल्य" शब्द की परिभाषा है:

जब किसी विशिष्ट प्रकार की व्याख्या की जाती है तो किसी वस्तु की सामग्री का सटीक अर्थ

(मेरे द्वारा जोर दिया गया।) हालांकि, सी में ऐसा कोई "पता प्रकार" नहीं है।

सूचक समान नहीं है। सी भाषा में पॉइंटर एक प्रकार का प्रकार है। कई अलग-अलग सूचक प्रकार हैं। वे आवश्यक रूप से भाषा के नियमों के समान सेट का पालन नहीं करते हैं, जैसे ++प्रकार int*बनाम के मूल्य पर प्रभाव char*

C में एक मान एक पॉइंटर प्रकार का हो सकता है। इसे पॉइंटर वैल्यू कहा जाता है । स्पष्ट होने के लिए, सी भाषा में पॉइंटर पॉइंटर पॉइंटर नहीं है। लेकिन हम उन्हें एक साथ मिलाने के आदी हैं, क्योंकि सी में यह अस्पष्ट होने की संभावना नहीं है: अगर हम एक अभिव्यक्ति pको "पॉइंटर" कहते हैं, तो यह केवल एक पॉइंटर वैल्यू है, लेकिन एक प्रकार नहीं है, क्योंकि सी में एक नामित प्रकार नहीं है एक अभिव्यक्ति द्वारा व्यक्त की जाती है , लेकिन एक प्रकार-नाम या एक टाइप-बीफ़-नाम से

कुछ अन्य चीजें सूक्ष्म हैं। C उपयोगकर्ता के रूप में, सबसे पहले, किसी को पता होना चाहिए कि objectइसका क्या मतलब है:

निष्पादन वातावरण में डेटा भंडारण का क्षेत्र, जिनमें से सामग्री मूल्यों का प्रतिनिधित्व कर सकती है

ऑब्जेक्ट मूल्यों का प्रतिनिधित्व करने के लिए एक इकाई है, जो एक विशिष्ट प्रकार के होते हैं। पॉइंटर एक ऑब्जेक्ट प्रकार है । इसलिए यदि हम घोषणा करते हैं int* p;, तो pइसका अर्थ है "सूचक प्रकार की वस्तु", या "सूचक वस्तु"।

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

चूँकि पॉइंटर एक प्रकार है, और एड्रेस सी में प्रभावी रूप से "टाइपलेस" है, एक पॉइंटर वैल्यू में लगभग एक एड्रेस होता है। और सूचक प्रकार की अभिव्यक्ति कर सकते हैं उपज एक पते, जैसे

आईएसओ सी 11 6.5.2.3

3 यूनिरी &ऑपरेटर अपने ऑपरेंड के पते की पैदावार करता है।

ध्यान दें कि यह शब्द WG14 / N1256, ISO C99: TC3 द्वारा प्रस्तुत किया गया है। C99 में है

3 अपर &संचालक अपने ऑपरेंड का पता देता है।

यह समिति की राय को दर्शाता है: एक पता, एक गैर-& संचालक द्वारा लौटाया गया सूचक मान नहीं है ।

उपरोक्त शब्दों के बावजूद, मानकों में अभी भी कुछ गड़बड़ है।

आईएसओ सी 11 6.6

9 एक स्थिरांक स्थिरांक एक अशक्त सूचक है, एक संकेतन एक आवेग का है जो स्थिर भंडारण अवधि या वस्तु सूचक के लिए एक सूचक नामित करता है।

आईएसओ सी ++ 11 5.19

3 ... एक एड्रेस कंटीन्यूअस एक्सप्रेशन, पॉइंटर टाइप का एक प्रील्यूव कोर कंटीन्यूअस एक्सप्रेशन है, जो किसी स्टेबल स्टोरेज अवधि के साथ किसी ऑब्जेक्ट के पते का मूल्यांकन करता है, किसी फंक्शन के पते पर, या एक न्यूल पॉइंटर वैल्यू, या एक प्रील्यू कोर कोर एक्सप्रेशन के लिए। प्रकार का std::nullptr_t। ...

(हाल ही में C ++ मानक ड्राफ्ट एक और शब्दांकन का उपयोग करता है ताकि कोई समस्या न हो।)

वास्तव में C में दोनों "एड्रेस कंटीन्यू" और C ++ में "एड्रेस कंटीन्यूअस एक्सप्रेशन" पॉइंटर टाइप्स (या C ++ 11 के बाद से कम से कम "पॉइंटर-लाइक" टाइप्स) की निरंतर अभिव्यक्ति हैं।

और अंतर्निहित यूनरी &ऑपरेटर को C और C ++ में "एड्रेस-ऑफ" कहा जाता है; इसी तरह, std::addressofC ++ 11 में पेश किया गया है।

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


2

यह कहता है "क्योंकि यह उन लोगों को भ्रमित करता है जो यह नहीं जानते कि पते किस बारे में हैं" - यह भी सच है: यदि आप सीखते हैं कि पते किस बारे में हैं, तो आप भ्रमित नहीं होंगे। सैद्धांतिक रूप से, सूचक एक चर है जो दूसरे को इंगित करता है, व्यावहारिक रूप से एक पता रखता है, जो उस चर का पता है जो इसे इंगित करता है। मुझे नहीं पता कि इस तथ्य को क्यों छिपाना चाहिए , यह रॉकेट साइंस नहीं है। यदि आप संकेत समझते हैं, तो आप यह समझने के लिए कि कंप्यूटर कैसे काम करता है, एक कदम और करीब आ जाएगा। आगे बढ़ें!


2

इसके बारे में सोचने के लिए आओ, मुझे लगता है कि यह शब्दार्थ की बात है। मुझे नहीं लगता कि लेखक सही है, क्योंकि सी मानक एक पॉइंटर को संदर्भित ऑब्जेक्ट के लिए एक पते के रूप में संदर्भित करता है क्योंकि अन्य ने पहले ही यहां उल्लेख किया है। हालाँकि, पता! = स्मृति पता। एक पता सी मानक के अनुसार वास्तव में कुछ भी हो सकता है, हालांकि यह अंततः एक स्मृति पते की ओर ले जाएगा, सूचक स्वयं एक आईडी, एक ऑफसेट + चयनकर्ता (x86) हो सकता है, वास्तव में कुछ भी जब तक यह वर्णन कर सकता है (मैपिंग के बाद) कोई भी स्मृति पता स्थान में पता।


एक पॉइंटर एक पता रखता है (या, अगर यह शून्य है)। लेकिन यह एक पता होने से बहुत दूर रो रहा है : उदाहरण के लिए, एक ही पते पर दो संकेत लेकिन एक अलग प्रकार के साथ कई स्थितियों में समान नहीं हैं।
गाइल्स 'एसओ- बुराई को रोकना'

@Gilles यदि आपको "जा रहा है", के रूप में int i=5-> मैं है 5 तब, सूचक पते हां में है। इसके अलावा, नल का एक पता भी है। आमतौर पर एक अमान्य लेखन पता (लेकिन जरूरी नहीं, x86- वास्तविक मोड देखें), लेकिन एक पता कोई कम नहीं। अशक्त के लिए वास्तव में केवल 2 आवश्यकताएं हैं: यह एक वास्तविक वस्तु के लिए एक सूचक के लिए असमान की तुलना करने की गारंटी है और किसी भी दो अशक्त बिंदु बराबर की तुलना करेगा।
वैलेंटाइन रादु

इसके विपरीत, एक शून्य सूचक किसी भी वस्तु के पते के बराबर नहीं होने की गारंटी है। अशक्त सूचक को अपरिभाषित करना अपरिभाषित व्यवहार है। यह कहने के साथ एक बड़ी समस्या है कि "सूचक पता है" यह है कि वे अलग तरह से काम करते हैं। यदि pएक पॉइंटर है, p+1तो हमेशा 1. द्वारा
गिना हुआ

फिर से पढ़ें टिप्पणी कृपया, it's guaranteed to compare unequal to a pointer to an actual object। सूचक अंकगणित के लिए, मैं बिंदु नहीं देखता, सूचक का मान अभी भी एक पता है, भले ही "+" ऑपरेशन जरूरी नहीं कि इसमें एक बाइट जोड़ देगा।
वैलेन्टिन रादू

1

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

कंपाइलर मान सकता है कि असंगत प्रकार के पॉइंटर्स एक ही पते पर इंगित नहीं करते हैं भले ही वे स्पष्ट रूप से करते हों, जो व्यवहार दे सकता है जो कि एक साधारण पॉइंटर == एड्रेस मॉडल के साथ संभव नहीं होगा। निम्नलिखित कोड पर विचार करें (मानकर sizeof(int) = 2*sizeof(short)):

unsigned int i = 0;
unsigned short* p = (unsigned short*)&i;
p[0]=p[1]=1;

if (i == 2 + (unsigned short)(-1))
{
  // you'd expect this to execute, but it need not
}

if (i == 0)
{
  // you'd expect this not to execute, but it actually may do so
}

ध्यान दें कि इसके लिए एक अपवाद है char*, इसलिए उपयोग करने वाले मान में हेरफेर char*संभव है (हालांकि बहुत पोर्टेबल नहीं है)।


0

त्वरित सारांश: एसी पता एक मूल्य है, जिसे आमतौर पर एक विशिष्ट प्रकार के साथ मशीन-स्तरीय मेमोरी पते के रूप में दर्शाया जाता है।

अयोग्य शब्द "पॉइंटर" अस्पष्ट है। C में पॉइंटर ऑब्जेक्ट्स (चर), पॉइंटर प्रकार , पॉइंटर एक्सप्रेशन और पॉइंटर मान हैं

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

सी मानक, कम से कम कुछ मामलों में, "पॉइंटर" शब्द का उपयोग "सूचक मान" करने के लिए करता है। उदाहरण के लिए, मॉलॉक का वर्णन यह कहता है कि "या तो एक शून्य सूचक या आवंटित स्थान के लिए एक संकेतक है"।

तो सी में क्या पता है? यह एक पॉइंटर वैल्यू है, यानी कुछ विशेष पॉइंटर टाइप का मान। (सिवाय इसके कि एक शून्य सूचक मान को "पता" के रूप में संदर्भित नहीं किया जाता है, क्योंकि यह किसी भी चीज़ का पता नहीं है)।

यूनिरी &ऑपरेटर के मानक विवरण में कहा गया है कि यह "अपने ऑपरेंड का पता देता है"। C मानक के बाहर, शब्द "पता" का उपयोग आमतौर पर (भौतिक या आभासी) मेमोरी पते को संदर्भित करने के लिए किया जाता है, आमतौर पर आकार में एक शब्द (किसी भी सिस्टम पर "शब्द" जो भी हो)।

एसी "पता" को आमतौर पर मशीन के पते के रूप में लागू किया जाता है - जिस तरह एक सी intवैल्यू को आमतौर पर मशीन शब्द के रूप में लागू किया जाता है। लेकिन एक सी एड्रेस (पॉइंटर वैल्यू) सिर्फ एक मशीन एड्रेस से ज्यादा है। यह आमतौर पर मशीन के पते के रूप में दर्शाया गया मूल्य है , और यह कुछ विशिष्ट प्रकार के साथ एक मूल्य है ।


0

एक सूचक मूल्य है एक पते। एक सूचक चर है एक वस्तु है कि एक पते स्टोर कर सकते हैं। यह सच है क्योंकि यही मानक एक पॉइंटर को परिभाषित करता है। इसे सी novices को बताना महत्वपूर्ण है क्योंकि C novices अक्सर एक पॉइंटर और उस चीज़ के बीच अंतर पर स्पष्ट नहीं होते हैं जो यह इंगित करता है (यह कहना है, वे एक लिफाफे और एक इमारत के बीच का अंतर नहीं जानते हैं)। एक पते की धारणा (प्रत्येक वस्तु का एक पता होता है और यही एक पॉइंटर स्टोर होता है) महत्वपूर्ण है क्योंकि यह उस प्रकार को हल करता है।

हालांकि, मानक एक विशेष स्तर पर अमूर्त बातचीत करता है। वे लोग जो लेखक के बारे में बात करते हैं, जो "जानते हैं कि पते किस बारे में हैं", लेकिन जो सी के लिए नए हैं, उन्हें आवश्यक रूप से अमूर्त के एक अलग स्तर पर पते के बारे में पता होना चाहिए - शायद विधानसभा भाषा द्वारा। इस बात की कोई गारंटी नहीं है कि C कार्यान्वयन पतों के लिए समान प्रतिनिधित्व का उपयोग करता है जैसा कि CPUs opcodes उपयोग करता है (इस मार्ग में "स्टोर एड्रेस" के रूप में संदर्भित), जिसके बारे में ये लोग पहले से ही जानते हैं।

वह "पूरी तरह से उचित पता हेरफेर" के बारे में बात करता है। जहां तक ​​सी मानक का संबंध है, मूल रूप से "पूरी तरह से उचित पता हेरफेर" जैसी कोई चीज नहीं है। जोड़ बिंदुओं पर परिभाषित किया गया है और यह मूल रूप से है। ज़रूर, आप एक पॉइंटर को पूर्णांक में परिवर्तित कर सकते हैं, कुछ बिटवाइज़ या अंकगणित ऑप्स कर सकते हैं, और फिर इसे वापस कन्वर्ट कर सकते हैं। यह मानक द्वारा काम करने की गारंटी नहीं है, इसलिए उस कोड को लिखने से पहले आप बेहतर जानते हैं कि आपका विशेष सी कार्यान्वयन पॉइंटर्स का प्रतिनिधित्व कैसे करता है और उस रूपांतरण को निष्पादित करता है। यह संभवतः आपके द्वारा अपेक्षित पता प्रतिनिधित्व का उपयोग करता है, लेकिन ऐसा नहीं है कि यह आपकी गलती है क्योंकि आपने मैनुअल नहीं पढ़ा है। यह भ्रम नहीं है, यह गलत प्रोग्रामिंग प्रक्रिया है ;-)

संक्षेप में, सी एक पते की अधिक अमूर्त अवधारणा का उपयोग करता है जो लेखक करता है।

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

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


0

बस कहने के लिए संकेत वास्तव में विभाजन तंत्र का एक हिस्सा हैं, जो विभाजन के बाद रैखिक पते और फिर पेजिंग के बाद भौतिक पते पर अनुवाद करते हैं। वास्तव में भौतिक पते आपको राम से संबोधित किए जाते हैं।

       Selector  +--------------+         +-----------+
      ---------->|              |         |           |
                 | Segmentation | ------->|  Paging   |
        Offset   |  Mechanism   |         | Mechanism |
      ---------->|              |         |           |
                 +--------------+         +-----------+
        Virtual                   Linear                Physical
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.