सी में संरचनाओं को वापस करने वाले कई कार्य वास्तव में संरचनाओं की ओर संकेत क्यों करते हैं?


49

returnफ़ंक्शन के बयान में पूरे ढांचे को वापस करने के विपरीत एक संरचना को सूचक वापस करने का क्या फायदा है ?

मैं इस तरह के कार्यों fopenऔर अन्य निम्न स्तर के कार्यों के बारे में बात कर रहा हूं लेकिन शायद उच्च स्तर के कार्य हैं जो संरचनाओं के साथ-साथ संकेत भी लौटाते हैं।

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

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

एक पूर्ण संरचना लौटना जो NULLकठिन होगा मैं मान लेता हूं या कम कुशल। क्या यह एक वैध कारण है?


9
@ JohnR.Strohm मैंने इसे आज़माया और यह वास्तव में काम करता है। एक फ़ंक्शन एक संरचना को वापस कर सकता है .... तो क्या कारण है कि नहीं किया गया है?
योयो_फुन

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

26
FILE*प्रभावी रूप से एक अपारदर्शी संभाल है। उपयोगकर्ता कोड को यह ध्यान नहीं देना चाहिए कि इसकी आंतरिक संरचना क्या है।
कोडइन्चोस

3
जब आप कचरा संग्रह करते हैं तो संदर्भ द्वारा वापसी केवल एक उचित डिफ़ॉल्ट है।
इदान आर्य

6
@ JohnR.Strohm आपकी प्रोफ़ाइल में "बहुत वरिष्ठ" 1989 से पहले वापस जाने के लिए लगता है ;-) - जब ANSI C ने K & R C को अनुमति नहीं दी थी: असाइनमेंट, कॉपी पासिंग और रिटर्न मान में स्ट्रक्चर कॉपी करें। K & R की मूल पुस्तक वास्तव में स्पष्ट रूप से बताई गई है (मैं विरोधाभास कर रहा हूं): "आप संरचना के साथ वास्तव में दो काम कर सकते हैं, इसके साथ & एक सदस्य का पता लगा सकते हैं और उसके साथ पहुंच सकते हैं .।"
पीटर - मोनिका

जवाबों:


61

कई व्यावहारिक कारण हैं कि प्रकार fopenके उदाहरणों के बजाय रिटर्न पॉइंटर्स जैसे फ़ंक्शन क्यों structहैं:

  1. आप structउपयोगकर्ता से प्रकार का प्रतिनिधित्व छिपाना चाहते हैं ;
  2. आप गतिशील रूप से एक वस्तु आवंटित कर रहे हैं;
  3. आप किसी ऑब्जेक्ट के एक ही उदाहरण को कई संदर्भों के माध्यम से संदर्भित कर रहे हैं;

जैसे प्रकार के मामले में FILE *, यह इसलिए है क्योंकि आप उपयोगकर्ता को प्रकार के प्रतिनिधित्व के विवरण को उजागर नहीं करना चाहते हैं - एक FILE *ऑब्जेक्ट एक अपारदर्शी हैंडल के रूप में कार्य करता है, और आप बस उस हैंडल को विभिन्न I / O रूटीन में (और FILEअक्सर जबकि ) पास करते हैं एक structप्रकार के रूप में कार्यान्वित , यह होना जरूरी नहीं है )।

तो, आप एक शीर्ष लेख में अधूरे struct प्रकार को कहीं भी उजागर कर सकते हैं :

typedef struct __some_internal_stream_implementation FILE;

जब आप एक अपूर्ण प्रकार के उदाहरण की घोषणा नहीं कर सकते, तो आप इसे एक सूचक घोषित कर सकते हैं। तो मैं एक बना सकते हैं FILE *और इसे करने के लिए के माध्यम से आवंटित fopen, freopenआदि,, लेकिन मैं सीधे वस्तु यह की ओर इशारा करता हेरफेर नहीं कर सकते हैं।

यह भी संभावना है कि fopenफ़ंक्शन FILEगतिशील mallocया समान रूप से किसी ऑब्जेक्ट को आवंटित कर रहा है । उस स्थिति में, यह सूचक को वापस करने के लिए समझ में आता है।

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


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

6
@Barmar: दरअसल, एबीआई स्थिरता है सी के बड़ा विक्रय बिंदु है, और यह अपारदर्शी संकेत के बिना के रूप में स्थिर नहीं होगा।
मैथ्यू एम।

37

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

सबसे पहले, एक संरचना की नकल एक सूचक की नकल करने की तुलना में बहुत अधिक सीपीयू समय लेता है। यदि यह ऐसा कुछ है जो आपका कोड बार-बार करता है, तो यह ध्यान देने योग्य प्रदर्शन अंतर पैदा कर सकता है।

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


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

17
इसके अलावा, इन दिनों संकेत अधिकांश डेस्कटॉप और सर्वर प्लेटफार्मों पर 64 बिट्स हैं। मैंने अपने करियर में कुछ से अधिक स्ट्रक्चर्स देखे हैं जो 64 बिट्स में फिट होंगे। इसलिए, आप हमेशा यह नहीं कह सकते हैं कि एक पॉइंटर को कॉपी करने से किसी स्ट्रक्चर को कॉपी करने में कम खर्च होता है।
सोलोमन स्लो

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

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

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

12

अन्य उत्तरों के अलावा, कभी-कभी मूल्य द्वारा एक छोटा लौटना structसार्थक होता है। उदाहरण के लिए, कोई एक डेटा, और उससे संबंधित कुछ त्रुटि (या सफलता) कोड वापस कर सकता है।

एक उदाहरण लेने के लिए, fopenकेवल एक डेटा (खोला FILE*) लौटाता है और त्रुटि के मामले में, त्रुटि कोड को errnoछद्म वैश्विक चर के माध्यम से देता है । लेकिन structदो सदस्यों में से एक को वापस करना शायद बेहतर होगा : FILE*हैंडल, और त्रुटि कोड (जो फ़ाइल हैंडल होने पर सेट किया जाएगा NULL)। ऐतिहासिक कारणों से ऐसा नहीं है (और त्रुटियों को errnoवैश्विक रूप से बताया जाता है, जो आज एक स्थूल है)।

ध्यान दें कि गो भाषा में दो (या कुछ) मान लौटाने के लिए एक अच्छी संकेतन है।

यह भी देखें कि, लिनक्स / x86-64 पर ABI और कॉलिंग कन्वेंशन (देखें x86-psABI पेज) निर्दिष्ट करता है कि structदो स्केलर सदस्यों में से एक (जैसे एक पॉइंटर और एक पूर्णांक, या दो पॉइंटर्स, या दो पूर्णांक दो रजिस्टरों के माध्यम से लौटाए जाते हैं) (और यह बहुत कुशल है और स्मृति के माध्यम से नहीं जाता है)।

इसलिए नए C कोड में, एक छोटा C वापस structकरना अधिक पठनीय, अधिक थ्रेड-फ्रेंडली और अधिक कुशल हो सकता है।


वास्तव में छोटी संरचना में पैक किया जाता है rdx:rax। तो struct foo { int a,b; };लौटा दिया जाता है rax(जैसे शिफ्ट / या के साथ), और इसे शिफ्ट / मूव से अनपैक करना होगा। यहाँ Godbolt पर एक उदाहरण है । लेकिन x86 उच्च बिट्स की परवाह किए बिना 32-बिट संचालन के लिए 64-बिट रजिस्टर के कम 32 बिट्स का उपयोग कर सकता है, इसलिए यह हमेशा बहुत खराब होता है, लेकिन निश्चित रूप से 2-सदस्यीय संरचनाओं के लिए 2 रजिस्टरों का उपयोग करने से भी बदतर है।
पीटर कॉर्ड्स

संबंधित: Bugs.llvm.org/show_bug.cgi?id=34840 std::optional<int> शीर्ष के आधे भाग में बूलियन देता है rax, इसलिए आपको इसे परीक्षण करने के लिए 64-बिट मास्क की आवश्यकता होती है test। या आप उपयोग कर सकते हैं bt। लेकिन यह कॉलर और केली की तुलना में बेकार है dl, जो "निजी" कार्यों के लिए कंपाइलरों का उपयोग करना चाहिए। यह भी संबंधित है: libstdc ++ का std::optional<T>टी-होने पर भी तुच्छ नहीं है, इसलिए यह हमेशा छिपे हुए सूचक के माध्यम से वापस आ जाता है: stackoverflow.com/questions/46544019/… । (libc ++ का तुच्छ प्रतिरूप है)
पीटर कॉर्ड्स

@PeterCordes: आपकी संबंधित चीजें C ++ हैं, न कि C
बेसिल स्टायरेंविच

उफ़, सही है। खैर एक ही बात लागू होगा वास्तव में करने के लिए struct { int a; _Bool b; };सी में, अगर फोन करने वाले, बूलियन परीक्षण करने के लिए क्योंकि तुच्छता-copyable सी ++ structs सी के रूप में ही ABI का उपयोग करना चाहता था
पीटर Cordes

1
क्लासिक उदाहरणdiv_t div()
chux -

6

आप सही रास्ते पर हैं

आपके द्वारा उल्लिखित दोनों कारण मान्य हैं:

कारणों में से एक मैंने सोचा था कि एक संरचना में सूचक को वापस करने के लिए एक फायदा होगा, यदि NULL सूचक लौटाकर फ़ंक्शन विफल हो जाता है तो अधिक आसानी से बताने में सक्षम होगा।

एक पूर्ण संरचना लौटना जो कि NULL है मैं कठिन या कम कुशल होगा। क्या यह एक वैध कारण है?

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

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

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

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

इसके अलावा, टिप्पणियों में मैं देख रहा हूं कि एक सवाल है कि क्या आप किसी फ़ंक्शन से एक संरचना वापस कर सकते हैं। आप वास्तव में ऐसा कर सकते हैं। निम्नलिखित काम करना चाहिए:

struct s1 {
   int integer;
};

struct s1 f(struct s1 input){
   struct s1 returnValue = xinput
   return returnValue;
}

int main(void){
   struct s1 a = { 42 };
   struct s1 b= f(a);

   return 0;
}

यह कैसे संभव है पता नहीं है कि एक निश्चित चर की कितनी स्मृति की आवश्यकता होगी यदि आपके पास पहले से निर्धारित संरचना प्रकार है?
yoyo_fun 14

9
@ जेनिफरएंडरसन सी में अपूर्ण प्रकार की अवधारणा है: एक प्रकार का नाम घोषित किया जा सकता है लेकिन अभी तक परिभाषित नहीं किया गया है, इसलिए इसका आकार अनुपलब्ध है। मैं उस प्रकार के चर घोषित नहीं कर सकता, लेकिन उस प्रकार की ओर संकेत कर सकता हूं , जैसे struct incomplete* foo(void)। इस तरह मैं एक हेडर में फ़ंक्शंस की घोषणा कर सकता हूं, लेकिन केवल सी फ़ाइल के भीतर संरचनाओं को परिभाषित कर सकता हूं, इस प्रकार एनकैप्सुलेशन की अनुमति देता है।
आमोन

@amon तो यह है कि वे कार्य हेडर्स (प्रोटोटाइप / हस्ताक्षर) की घोषणा करने से पहले यह घोषणा करते हैं कि वे वास्तव में C में कैसे काम करते हैं? और सी
yoyo_fun

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

6

FILE*जहां तक ​​क्लाइंट कोड का संबंध है, संरचना की तरह कुछ भी वास्तव में एक संकेतक नहीं है, लेकिन इसके बजाय फाइल की तरह कुछ अन्य इकाई से जुड़े अपारदर्शी पहचानकर्ता का एक रूप है । जब कोई प्रोग्राम कॉल करता है fopen, तो यह आम तौर पर लौटी संरचना के किसी भी सामग्री के बारे में परवाह नहीं करेगा - यह सब इस बारे में परवाह करेगा कि अन्य कार्य जैसे कि freadवे इसके साथ क्या करना चाहते हैं, वह करेंगे।

यदि एक मानक पुस्तकालय FILE*उस फ़ाइल के भीतर वर्तमान पढ़ने की स्थिति जैसे किसी जानकारी के भीतर रखता है , तो freadउस जानकारी को अपडेट करने के लिए कॉल करने की आवश्यकता होगी। होने freadके लिए सूचक प्राप्त FILEकरता है कि आसान। यदि freadइसके बजाय एक प्राप्त होता है FILE, तो इसके पास FILEकॉलर द्वारा रखी गई वस्तु को अपडेट करने का कोई तरीका नहीं होगा ।


3

सूचना छिपाना

फ़ंक्शन के रिटर्न स्टेटमेंट में पूरी संरचना को वापस करने के विपरीत एक संरचना में सूचक को लौटने का क्या फायदा है?

सबसे आम एक जानकारी छिपाना है । C के पास structनिजी क्षेत्र बनाने की क्षमता नहीं है, कहते हैं, उन्हें एक्सेस करने के लिए अकेले तरीके प्रदान करें।

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

बाइनरी संगतता

संरचना की परिभाषा को छिपाने से डायलीब एपीआई में द्विआधारी संगतता को बनाए रखने के लिए सांस लेने में मदद मिल सकती है। यह पुस्तकालय कार्यान्वयनकर्ताओं को पुस्तकालय का उपयोग करने वालों के साथ द्विआधारी संगतता को तोड़ने के बिना अपारदर्शी संरचना में फ़ील्ड्स को बदलने की अनुमति देता है, क्योंकि उनके कोड की प्रकृति को केवल यह जानना है कि वे संरचना के साथ क्या कर सकते हैं, न कि यह कितना बड़ा है या क्या फ़ील्ड है। यह है।

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

दक्षता

एक पूर्ण संरचना लौटना जो कि NULL है, मैं कठिन या कम कुशल होगा। क्या यह एक वैध कारण है?

यह आम तौर पर कम कुशल है, यह मानते हुए कि प्रकार को व्यावहारिक रूप से फिट किया जा सकता है और स्टैक पर आवंटित किया जा सकता है जब तक कि आम तौर पर पर्दे के पीछे इस्तेमाल किए जाने वाले सामान्यीकृत मेमोरी आवंटन की mallocतरह नहीं होता , जैसे कि पहले से आवंटित चर-आकार के आवंटन पूलिंग मेमोरी के बजाय एक निश्चित आकार। यह इस मामले में एक सुरक्षा व्यापार बंद है, सबसे अधिक संभावना है, पुस्तकालय डेवलपर्स को संबंधित से संबंधित वैचारिक (वैचारिक गारंटी) बनाए रखने की अनुमति देता है FILE

यह कम से कम एक प्रदर्शन के दृष्टिकोण से fopenएक पॉइंटर को वापस करने के लिए ऐसा कोई वैध कारण नहीं है क्योंकि यह एकमात्र कारण है जो NULLकिसी फ़ाइल को खोलने में विफलता पर है। यह सभी सामान्य-मामले निष्पादन मार्गों को धीमा करने के बदले में एक असाधारण परिदृश्य का अनुकूलन होगा। कुछ मामलों में मान्य उत्पादकता का कारण हो सकता है कि वे डिज़ाइन को अधिक सीधा बनाने के लिए उन्हें NULLकुछ पोस्ट-स्थिति पर वापस लौटने की अनुमति देने के लिए वापसी बिंदु बनाते हैं।

फ़ाइल संचालन के लिए, ओवरहेड फ़ाइल के संचालन की तुलना में अपेक्षाकृत अधिक तुच्छ है, और मैनुअल को fcloseकिसी भी तरह से टाला नहीं जा सकता है। इसलिए ऐसा नहीं है कि हम ग्राहक को परिभाषा को मुक्त करने (बंद करने) की परेशानी से बचा सकते हैं FILEऔर परिभाषा को उजागर fopenकर सकते हैं या एक ढेर आवंटन से बचने के लिए फ़ाइल के संचालन की सापेक्ष लागत को देखते हुए बहुत अधिक प्रदर्शन को बढ़ावा देने की उम्मीद करते हैं। ।

हॉटस्पॉट्स और फिक्सेस

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

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

struct Foo
{
   /* priv_* indicates that you shouldn't tamper with these fields! */
   int priv_internal_field;
   int priv_other_one;
};

struct Foo foo_create(void);
void foo_destroy(struct Foo* foo);
void foo_something(struct Foo* foo);

यदि भविष्य में द्विआधारी संगतता चिंताएं हैं, तो मैंने इसे बहुत बेहतर पाया है ताकि भविष्य के प्रयोजनों के लिए कुछ अतिरिक्त स्थान आरक्षित कर सकें, जैसे:

struct Foo
{
   /* priv_* indicates that you shouldn't tamper with these fields! */
   int priv_internal_field;
   int priv_other_one;

   /* reserved for possible future uses (emergency backup plan).
     currently just set to null. */
   void* priv_reserved;
};

यह आरक्षित स्थान थोड़ा व्यर्थ है, लेकिन भविष्य में अगर हम Fooअपने पुस्तकालय का उपयोग करने वाले बायनेरिज़ को तोड़ने के बिना कुछ और डेटा जोड़ने की आवश्यकता है, तो हम एक जीवन रक्षक हो सकते हैं ।

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

int foo_create(struct Foo* foo);
...
/* In the client code: */
struct Foo foo;
if (foo_create(&foo))
{
    foo_something(&foo);
    foo_destroy(&foo);
}

... Fooएक शानदार प्रतिलिपि की संभावना के बिना स्टैक से आरंभ करने के लिए । या ग्राहक को भी Fooढेर पर आवंटित करने की स्वतंत्रता है अगर वे किसी कारण से चाहते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.