2010-05-14 24 views
34

मुझे डेरफ़्रेंसिंग ऑपरेटर, ऑपरेटर का पता, और सामान्य रूप से पॉइंटर्स की काफी अच्छी समझ है।सी ++ रिटर्निंग पॉइंटर्स/संदर्भ

मैं हालांकि इस जैसे जब मैं चीजें ही दिखाई भ्रमित हो:

int* returnA() { 
    int *j = &a; 
    return j; 
} 

int* returnB() { 
    return &b; 
} 

int& returnC() { 
    return c; 
} 

int& returnC2() { 
    int *d = &c; 
    return *d; 
} 
  1. returnA() मैं एक सूचक वापस जाने के लिए पूछ रहा हूँ में; बस इस काम को स्पष्ट करने के लिए क्योंकि j एक सूचक है?
  2. returnB() में मैं एक सूचक वापस करने के लिए कह रहा हूं; चूंकि एक सूचक एक पते पर इंगित करता है, कारण returnB() काम करता है क्योंकि मैं &b लौट रहा हूं?
  3. returnC() में मैं लौटने के लिए int का पता मांग रहा हूं। जब मैं c लौटाता हूं & ऑपरेटर स्वचालित रूप से c "संलग्न" होता है?
  4. returnC2() में मैं वापस लौटने के लिए int के पते के लिए फिर से पूछ रहा हूं। *d काम करता है क्योंकि पॉइंटर्स किसी पते पर इंगित करते हैं?

मान लें कि, बी, सी को ग्लोबल के रूप में पूर्णांक के रूप में प्रारंभ किया गया है।

क्या कोई यह सत्यापित कर सकता है कि मैं अपने सभी चार प्रश्नों के साथ सही हूं या नहीं?

+2

इन सवालों के जवाब पूरी तरह निर्भर ए, बी, और सी की घोषणाओं पर, जो कोड से गायब लगते हैं। –

+0

मान लें कि ए, बी, सी शुरू हो गए हैं। –

+0

चर इंक हैं। –

उत्तर

22

बदले में() मैं एक सूचक वापस करने के लिए कह रहा हूं; बस इस काम को स्पष्ट करने के लिए क्योंकि जे एक सूचक है?

हाँ, int *j = &aa को इंगित करने के initializes j। फिर आप j का मान वापस करते हैं, जो a का पता है।

बदले में() मैं एक सूचक वापस करने के लिए कह रहा हूं; चूंकि एक सूचक एक पते पर इंगित करता है, कारण वापसीब() काम करता है क्योंकि मैं & बी वापस कर रहा हूं?

हां। यहां एक ही चीज ऊपर की तरह होती है, बस एक ही चरण में। &bb का पता देता है।

बदले में() मैं वापस आने के लिए एक इंटेल के पते के लिए पूछ रहा हूं। जब मैं सी वापस करता हूं & ऑपरेटर स्वचालित रूप से संलग्न होता है?

नहीं, यह एक इंट का संदर्भ है जो लौटाया जाता है। एक संदर्भ एक संकेत नहीं है जैसा कि सूचक है - यह एक चर के लिए सिर्फ एक वैकल्पिक नाम है। इसलिए आपको चर के संदर्भ प्राप्त करने के लिए & ऑपरेटर को लागू करने की आवश्यकता नहीं है।

बदले में सी 2() मैं फिर से वापस आने के पते के लिए पूछ रहा हूं। क्या * काम करता है क्योंकि पॉइंटर्स किसी पते पर इंगित करते हैं?

फिर से, यह एक इंट का संदर्भ है जो लौटाया जाता है। *d मूल चर c (जो भी हो सकता है) को संदर्भित करता है, c द्वारा इंगित किया गया है।और यह स्पष्ट रूप से संदर्भ में बदल दिया जा सकता है, जैसा कि returnC में है।

पॉइंटर्स सामान्य पते पर एक पते पर नहीं हैं (हालांकि वे कर सकते हैं - उदाहरण के लिए int** int के सूचक के लिए एक सूचक है)। पॉइंटर्स कुछ का पता है। जब आप something* जैसे पॉइंटर घोषित करते हैं, तो something वह चीज़ है जो आपका पॉइंटर इंगित करता है। तो मेरे उपर्युक्त उदाहरण में, int**int* पर एक पॉइंटर घोषित करता है, जो एक पॉइंटर होता है।

+0

क्या आप तीसरे मुद्दे (रिटर्नसी() फ़ंक्शन) का सुसंगत उत्तर प्रदान कर सकते हैं? भले ही सी संदर्भ चर नहीं है, फिर भी फ़ंक्शन सी के रूप में स्वीकार करता है क्योंकि यह संदर्भों की मांग करता है और एक पूर्णांक चर वापस लौटाया जाता है? – chosentorture

+0

@ChosenTorture, मुझे लगता है कि टायलर ने नीचे दिए गए अपने उत्तर में इसे बहुत अच्छी तरह से समझाया है, इसलिए मैं इसे ऊपर उठाता हूं और DRY सिद्धांत का पालन करता हूं :-) –

+0

मैंने पढ़ा है कि टायलर ने ध्यान से क्या लिखा है। या तो वह मेरे प्रश्न का उत्तर नहीं देता है या शायद मैं कुछ समझ नहीं रहा हूं। मैं आपको जो लिख रहा हूं उसे पढ़ने का अनुरोध करता हूं। आपने लिखा है कि नहीं, नहीं, यह एक int का संदर्भ है जो वापस किया गया है_। तो जब मैं एक कार्य प्रकार 'int' 'करता हूं, तो क्या यह प्रोग्रामर को बताता है कि एक संदर्भ ** ** ** वापस किया जाना चाहिए या एक संदर्भ ** होना चाहिए और ** वापस किया जाएगा? यदि पूर्व मामला सत्य है तो कोड काम नहीं करना चाहिए। लेकिन क्योंकि यह करता है, बाद वाला सच है। मैं बस इतना पूछ रहा हूं कि क्यों? क्या यह एक अंतर्निहित रूपांतरण प्रक्रिया है या कुछ? – chosentorture

2

बदले में() और returnC2() आप पते को वापस करने के लिए नहीं कह रहे हैं।

ये दोनों कार्य ऑब्जेक्ट्स के संदर्भ वापस करते हैं।
एक संदर्भ किसी भी चीज़ का पता नहीं है, यह किसी का वैकल्पिक नाम है (इसका मतलब यह हो सकता है कि संकलक वस्तु (या स्थिति के आधार पर नहीं हो सकता) ऑब्जेक्ट का प्रतिनिधित्व करने के लिए किसी पते का उपयोग कर सकता है (वैकल्पिक रूप से इसे रजिस्टर में रखना भी जान सकता है))।

आप सभी जानते हैं कि एक विशिष्ट वस्तु पर संदर्भ बिंदु।
जबकि एक संदर्भ स्वयं एक वस्तु नहीं है एक वैकल्पिक नाम है।

+0

यह वह जगह है जहां मैं उलझन में था; मैंने सोचा कि मैं पते वापस कर रहा था; नहीं "उपनाम"। –

1

आपके सभी उदाहरण अपरिभाषित रन-टाइम व्यवहार उत्पन्न करते हैं। निष्पादन समारोह को छोड़ने के बाद गायब होने वाले आइटमों के लिए आप पॉइंटर्स या संदर्भ लौट रहे हैं।

मुझे स्पष्ट करते हैं:

int * returnA() 
{ 
    static int a; // The static keyword keeps the variable from disappearing. 
    int * j = 0; // Declare a pointer to an int and initialize to location 0. 
    j = &a;  // j now points to a. 
    return j;  // return the location of the static variable (evil). 
} 

अपने कार्य में, चर ja के अस्थायी स्थान को इंगित करने के लिए आवंटित किया गया है। आपके फ़ंक्शन के बाहर निकलने पर परिवर्तनीय a गायब हो जाता है, लेकिन यह पूर्व स्थान j के माध्यम से वापस किया जाता है। चूंकि aj की ओर इशारा करते हुए स्थान पर मौजूद नहीं है, इसलिए *j तक पहुंचने के साथ अपरिभाषित व्यवहार होगा।

कार्यों के अंदर चर को अन्य कोड द्वारा संदर्भ या सूचक के माध्यम से संशोधित नहीं किया जाना चाहिए। ऐसा हो सकता है हालांकि यह अपरिभाषित व्यवहार पैदा करता है।

पेडेंटिक होने के कारण, पॉइंटर्स लौटाए जाने वाले पॉइंटर्स निरंतर डेटा को इंगित करने के रूप में घोषित किए जाने चाहिए। लौटा संदर्भ संदर्भ होना चाहिए:

const char * Hello() 
{ 
    static const char text[] = "Hello"; 
    return text; 
} 

उपरोक्त फ़ंक्शन निरंतर डेटा के लिए एक सूचक देता है। अन्य कोड स्थैतिक डेटा तक पहुंच (पढ़ सकते हैं) लेकिन संशोधित नहीं किया जा सकता है।

const unsigned int& Counter() 
{ 
    static unsigned int value = 0; 
    value = value + 1; 
    return value; 
} 

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

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

+0

यदि आप मानते हैं कि 'ए', 'बी', और' सी' ग्लोबल्स हैं, तो नमूना कोड ठीक है। अगर उन्हें कार्यों के भीतर सांख्यिकी के रूप में घोषित किया गया था, तो यह भी ठीक होगा। स्थैतिक स्थानों को अस्थायी स्थानों में स्थानांतरित नहीं किया जाता है। उनके स्थान स्थिर हैं। आप हमेशा एक सूचक या स्थिर चर के संदर्भ को वापस कर सकते हैं। यह हमेशा एक अच्छा विचार नहीं है (पुन: प्रवेश और बहुप्रचार), लेकिन यह काम करता है। –

1

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

इसका नकारात्मक पक्ष यह है कि आप जो भी संदर्भित करते हैं उसे संशोधित नहीं कर सकते हैं - वे निर्माण समय पर बंधे हैं।

60

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

  • *, जब एक प्रकार के हिस्से के रूप में इस्तेमाल किया इंगित करता है कि प्रकार एक सूचक है: int एक प्रकार है, तो int* एक सूचक करने वाली पूर्णांक प्रकार है, और int** एक है पॉइंटर-टू-पॉइंटर-टू-इंट टाइप।

  • &प्रकार के हिस्से के रूप में उपयोग किए जाने पर इंगित करता है कि यह प्रकार एक संदर्भ है। int एक प्रकार है, इसलिए int& एक संदर्भ-से-int है (संदर्भ-संदर्भ के लिए ऐसी कोई चीज़ नहीं है)। संदर्भ और पॉइंटर्स समान चीजों के लिए उपयोग किए जाते हैं, लेकिन वे काफी अलग हैं और अंतरण योग्य नहीं हैं। मौजूदा संदर्भ के लिए एक संदर्भ उपनाम, या वैकल्पिक नाम के रूप में सबसे अच्छा विचार है। यदि x एक int है, तो आप के लिए नया नाम y बनाने के लिए बस int& y = x असाइन कर सकते हैं। बाद में, x और y को एक ही पूर्णांक के संदर्भ में एक दूसरे के लिए उपयोग किया जा सकता है। इसका दो मुख्य प्रभाव यह है कि संदर्भ न्यूल नहीं हो सकते हैं (क्योंकि संदर्भ में मूल चर होना चाहिए), और मूल मूल्य प्राप्त करने के लिए आपको किसी विशेष ऑपरेटर का उपयोग करने की आवश्यकता नहीं है (क्योंकि यह केवल एक वैकल्पिक नाम है, एक सूचक नहीं)। संदर्भों को फिर से सौंपी जा सकती है।

  • * जब एक एकल ऑपरेटर के रूप में इस्तेमाल एक ऑपरेशन कहा जाता भिन्नता करता है (जो संदर्भ प्रकार साथ कोई संबंध नहीं है!)। यह ऑपरेशन पॉइंटर्स पर केवल सार्थक है। जब आप एक पॉइंटर को कम करते हैं, तो आप इसे वापस प्राप्त करते हैं। इसलिए, यदि p एक पॉइंटर-टू-इंट है, *pint पर इंगित किया जा रहा है।

  • & जब एक एकल ऑपरेटर के रूप में इस्तेमाल एक ऑपरेशन पता के- बुलाया प्रदर्शन करती है। यह सुंदर आत्म-व्याख्यात्मक है; यदि x एक चर है, तो &xx का पता है। एक चर के पते को उस चर के प्रकार के लिए एक सूचक को असाइन किया जा सकता है। इसलिए, यदि x एक int है, तो &x को int* के सूचक के लिए असाइन किया जा सकता है, और वह सूचक x पर इंगित करेगा। जैसे यदि आप int* p = &x असाइन करते हैं, तो *p का उपयोग x के मान को पुनर्प्राप्त करने के लिए किया जा सकता है।

तो याद है, प्रकार प्रत्यय & संदर्भ के लिए है, और एकल operatory &, संकेत के साथ प्रयोग के लिए पते हो रही के साथ क्या करना है जो कोई लेना देना नहीं है। दो उपयोग पूरी तरह से असंबंधित हैं। और * एक प्रकार के प्रत्यय के रूप में एक सूचक घोषित करता है, जबकि * एक यूनरी ऑपरेटर के रूप में पॉइंटर्स पर एक क्रिया करता है।

+6

इन ऑपरेटरों पर मैंने देखा है कि सबसे स्पष्ट स्पष्टीकरणों में से एक के लिए धन्यवाद। –

+0

बहुत स्पष्ट आपको बहुत धन्यवाद! – ChaoSXDemon

+0

अंत में एक समाधान देखें जो मुझे इन दो चीजों के लिए मेरे सिर को लपेटने में मदद कर सकता है – ryf9059

5

टायलर, कि बहुत मददगार विवरण था, मैं कुछ प्रयोग विजुअल स्टूडियो डीबगर का उपयोग कर इस अंतर को स्पष्ट करने आगे भी किया था: -

int sample = 90; 
int& alias = sample; 
int* pointerToSample = &sample; 

Name     Address      Type 
&alias    0x0112fc1c {90}    int * 
&sample    0x0112fc1c {90}    int * 
pointerToSample  0x0112fc1c {90}    int * 
*pointerToSample 90      int 
alias 90          int & 
&pointerToSample  0x0112fc04 {0x0112fc1c {90}} int * * 

मेमोरी लेआउट

PointerToSample  Sample/alias 
_______________......____________________ 
0x0112fc1c |   | 90 | 
___________|___.....__|________|_______... 

[0x0112fc04] ...  [0x0112fc1c 
संबंधित मुद्दे