2010-03-26 13 views
7

मैंने देखा कि सी में एक सामान्य idiom है जो एक सूचक को वापस करने के बजाय एक दूसरे तर्क के रूप में एक un-malloc एड पॉइंटर स्वीकार करने के लिए है। उदाहरण:पॉइंटर दूसरे तर्क के रूप में?

/*function prototype*/  
void create_node(node_t* new_node, void* _val, int _type); 

/* implementation */ 
node_t* n; 
create_node(n, &someint, INT) 

/* function prototype */ 
node_t* create_node(void* _val, int _type) 

/* implementation */ 
node_t* n = create_node(&someint, INT) 

फायदे और/या दोनों तरीकों में से नुकसान क्या हैं के बजाय?

धन्यवाद!

संपादित करें आपके उत्तरों के लिए धन्यवाद। पसंद 1 के लिए प्रेरणा अब मेरे लिए बहुत स्पष्ट हैं (और मुझे यह इंगित करना चाहिए कि पसंद के लिए पॉइंटर तर्क 1 malloc'd जो मैंने मूल रूप से सोचा था) के विपरीत होना चाहिए।

उत्तर

15

एक सूचक स्मृति में भरे जाने वाले (जो फोन करने वाले malloc'ing या नहीं के लिए जिम्मेदार है) को स्वीकार करते हुए लौटने एक सूचक (जरूरी malloc'ed) से अधिक लचीलापन में गंभीर लाभ प्रदान करता है। विशेष रूप से, यदि कॉलर जानता है कि इसे किसी निश्चित फ़ंक्शन के भीतर जो कुछ भी लौटाया गया है, उसका उपयोग करने की आवश्यकता है, तो यह एक स्टैक-आवंटित संरचना या सरणी के पते में जा सकता है; अगर यह जानता है कि इसे पुनर्वितरण की आवश्यकता नहीं है, तो यह static संरचना या सरणी के पते में गुजर सकता है - किसी भी मामले में, एक मॉलोक/फ्री जोड़ी बचाई जाती है, और ऐसी बचत बढ़ जाती है! -)

+0

यह। 'संरचना चीज टी करने की क्षमता; init_thing (&t); 'अच्छा है। – dmckee

+0

यदि मैं एक लिंक्ड सूची की प्रतिलिपि बनाने के लिए एक फ़ंक्शन लिखना चाहता था, तो क्या मेरे पास' void copy_list (node_t * new_head, node_t * original_head) 'जैसे फ़ंक्शन प्रोटोटाइप होगा? मुझे लगता है कि मुझे malloc करना होगा मूल सूची में प्रत्येक के लिए नया नोड और इसे नए_हेड के 'अंत' में जोड़ें (वैल और प्रकार की प्रतिलिपि बनाएँ लेकिन इसे एक नया 'node_t * अगला' सूचक मान दें)। क्या यह इसके बारे में जाने का सबसे अच्छा तरीका है? – Tyler

+1

@ टायलर, उस प्रतिलिपि के लिए संभावित रूप से बेहतर हस्ताक्षर के रूप में 'copy_list (node_t ** original_head)' पर विचार करें (हालांकि यह असाधारण मामला _returning_ एक 'node_t *' निश्चित रूप से एक उचित संभावना है) –

0

व्यक्तिगत रूप से मैं refernce या सूचक पैराम का उपयोग कर डेटा वापस करना पसंद करता हूं, और त्रुटि कोड लौटने के लिए फ़ंक्शन रिटर्न का उपयोग करता हूं।

5

ज्यादा समझ में नहीं आता है। सी में पॉइंटर्स मूल्य से गुजरते हैं, बस अन्य वस्तुओं की तरह- अंतर मूल्य में निहित है। पॉइंटर्स के साथ, मान स्मृति पता है, जो फ़ंक्शन को पास किया जाता है। हालांकि, आप अभी भी मान को डुप्लिकेट कर रहे हैं, और इसलिए जब आप malloc, तो आप अपने फ़ंक्शन के अंदर पॉइंटर का मान बदल देंगे, न कि बाहरी पर।

void create_node(node_t* new_node, void* _val, int _type) { 
    new_node = malloc(sizeof(node_t) * SIZE); 
    // `new_node` points to the new location, but `n` doesn't. 
    ... 
} 

int main() { 
    ... 
    node_t* n = NULL; 
    create_node(n, &someint, INT); 
    // `n` is still NULL 
    ... 
} 

इससे बचने के तीन तरीके हैं। जैसा कि आपने बताया है, पहला है, फ़ंक्शन से नया पॉइंटर लौटा रहा है। दूसरा, सूचक के लिए सूचक ले जाने की जिससे संदर्भ द्वारा इसे पारित है:

void create_node(node_t** new_node, void* _val, int _type) { 
    *new_node = malloc(sizeof(node_t) * SIZE); 
    // `*new_node` points to the new location, as does `n`. 
    ... 
} 

int main() { 
    ... 
    node_t* n = NULL; 
    create_node(&n, &someint, INT); 
    // `n` points to the new location 
    ... 
} 

तीसरा है बस mallocn समारोह कॉल के बाहर:

int main() { 
    ... 
    node_t* n = malloc(sizeof(node_t) * SIZE); 
    create_node(n, &someint, INT); 
    ... 
} 
+0

मेरी समझ में उस बड़ी त्रुटि को सही करने के लिए धन्यवाद। :) – Tyler

+0

मुझे शायद यह जोड़ना चाहिए कि मैं एलेक्स मार्टेलि से सहमत हूं: तीसरी विधि निश्चित रूप से अधिकांश मामलों के लिए सबसे अच्छी है। –

2

मैं आमतौर पर संकेत प्राप्त पसंद करते हैं (संपत्ति प्रारंभिक) फंक्शन तर्क के रूप में एक सूचक क्षेत्र में एक सूचक को वापस करने के विरोध के रूप में जो समारोह के अंदर malloc'ed किया गया है। इस दृष्टिकोण के साथ आप स्पष्ट कर रहे हैं कि स्मृति प्रबंधन की जिम्मेदारी उपयोगकर्ता के पक्ष में है।

रिटर्निंग पॉइंटर्स आमतौर पर मेमोरी लीक की ओर जाता है, क्योंकि आपके पॉइंटर्स को मुफ्त में भूलना आसान होता है यदि आपने malloc() 'उन्हें संपादित नहीं किया है।

+0

"अगर आप malloc() 'ed ed" नहीं करते हैं तो अपने पॉइंटर्स को मुक्त करना भूलना आसान है। यकीन नहीं है कि मैं इसके साथ सहमत हूं, लेकिन मैंने एक सिस्टम पर 7 साल तक काम किया जो प्रक्रिया से बाहर निकलने पर स्मृति मुक्त नहीं करता था। यह आपको सिखाता है कि लीक-फ्री कोड कैसे लिखना है। –

+0

मेरा मतलब यह नहीं है ... "हमेशा", लेकिन शायद एक नौसिखिया कल्पना नहीं करता है कि फ़ंक्शन रिटर्न जो पॉइंटर लौटाता है उसके अंदर मॉलोकॉइड किया गया है। यह एक स्वाद-बात है:] – mgv

+0

@Steve सिस्टम क्या था और इसका क्या फायदा है? – Tyler

0

1) समीर के रूप में बताया कोड है गलत, सूचक मूल्य से पारित कर दिया है, आप की जरूरत **

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

3) आपका कार्य शून्य है, लेकिन इसे int वापस करना चाहिए ताकि यह त्रुटियों को वापस कर सके। कम से कम 2, शायद 3 संभावित त्रुटि स्थितियां होंगी: malloc विफल हो सकता है, तर्क तर्क अमान्य हो सकता है और संभवतः मूल्य सीमा से बाहर हो सकता है।

1

मैं आमतौर पर एक निश्चित विकल्प नहीं बनाता, मैं इसे अपनी पुस्तकालय में साफ करता हूं और दोनों दुनिया के सर्वश्रेष्ठ प्रदान करता हूं।

void node_init (node_t *n); 

void node_term (node_t *n); 

node_t *node_create() 
{ 
    node_t *n = malloc(sizeof *n); 
    /* boilerplate error handling for malloc returning NULL goes here */ 
    node_init(n); 
    return n; 
} 

void node_destroy (node_t *n) 
{ 
    node_term(n); 
    free(n); 
} 

हर malloc के लिए वहाँ एक मुक्त किया जाना चाहिए, हर init इस प्रकार के लिए एक शब्द नहीं होनी चाहिए और हर के लिए बनाने के एक नष्ट होना चाहिए। जैसे-जैसे आपकी वस्तुएं अधिक जटिल हो जाती हैं, आप पाएंगे कि आप उन्हें घोंसला शुरू कर देते हैं। कुछ उच्च स्तरीय ऑब्जेक्ट आंतरिक डेटा प्रबंधन के लिए node_t सूची का उपयोग कर सकते हैं। इस वस्तु को मुक्त करने से पहले, सूची पहले मुक्त होनी चाहिए। _init और _term इसके लिए देखभाल, पूरी तरह से इस कार्यान्वयन विस्तार छुपा।

अधिक जानकारी के बारे में निर्णय हो सकते हैं, उदा। नष्ट करने के बाद node_t ** n और n * को nULL सेट कर सकते हैं।

0

इस आलेख में चर्चा नहीं की गई एक समस्या यह है कि आप इसे आवंटित करने वाले फ़ंक्शन के भीतर मॉलोकॉइड बफर का जिक्र करने के बारे में कैसे बात करते हैं, और संभवतः, इसके कॉलर पर नियंत्रण लौटने से पहले इसमें कुछ स्टोर करता है।

मामले में जो मुझे इस पृष्ठ पर लाया, मेरे पास एक ऐसा फ़ंक्शन है जो पॉइंटर में गुजरता है, जो HOTKEY_STATE संरचनाओं की सरणी का पता प्राप्त करता है। प्रोटोटाइप निम्नानुसार तर्क घोषित करता है।

HOTKEY_STATE ** plplpHotKeyStates 

वापसी मान, ruintNKeys, सरणी, जो नियमित द्वारा निर्धारित किया जाता से पहले बफर आवंटित किया जाता है में तत्वों की संख्या है। मॉलोक() का उपयोग सीधे करने के बजाय, हालांकि, मैंने निम्नानुसार कॉलोक का उपयोग किया।

*plplpHotKeyStates = (HOTKEY_STATE *) calloc (ruintNKeys , 
               sizeof (HOTKEY_STATE)) ; 

के बाद इस बात की पुष्टि है कि plplpHotKeyStates नहीं रह गया है शून्य है, मैं, एक स्थानीय सूचक चर, hkHotKeyStates परिभाषित इस प्रकार है।

HOTKEY_STATE * hkHotKeyStates = *plplpHotKeyStates ; 

इस चर का उपयोग करना, एक सबस्क्रिप्ट के लिए एक अहस्ताक्षरित पूर्णांक के साथ, कोड संरचनाओं भरता है, सरल सदस्य ऑपरेटर (।) का उपयोग कर, जिन्हें आप नीचे।

hkHotKeyStates [ uintCurrKey ].ScanCode = SCANCODE_KEY_ALT ; 

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

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