एक-तर्क free(void *)
(यूनिक्स V7 में पेश किया गया) का पहले के दो-तर्क पर एक और बड़ा फायदा है mfree(void *, size_t)
जो मैंने यहां नहीं देखा है: एक तर्क free
नाटकीय रूप से हर दूसरे एपीआई को सरल करता है जो ढेर मेमोरी के साथ काम करता है। उदाहरण के लिए, यदि free
मेमोरी ब्लॉक के आकार की आवश्यकता है, तो strdup
किसी तरह एक (पॉइंटर) के बजाय दो मान (पॉइंटर + आकार) वापस करने होंगे, और सी एकल-मूल्य रिटर्न की तुलना में कई-कई गुना अधिक बोझिल बनाता है। इसके बजाय char *strdup(char *)
हमें लिखना होगा char *strdup(char *, size_t *)
वरना struct CharPWithSize { char *val; size_t size}; CharPWithSize strdup(char *)
। (आजकल वह दूसरा विकल्प बहुत आकर्षक लग रहा है, क्योंकि हम जानते हैं कि एनयूएल-टर्मिनेटेड स्ट्रिंग्स "कंप्यूटिंग के इतिहास में सबसे भयावह डिजाइन बग" हैं, लेकिन यह एक दृष्टिहीनता है। 70 के दशक में वापस, सी को एक साधारण के रूप में तार को संभालने की क्षमता char *
वास्तव में पास्कल और अल्गोल जैसे प्रतियोगियों पर एक परिभाषित लाभ माना जाता था ।) प्लस, यह सिर्फ strdup
इस समस्या से ग्रस्त नहीं है - यह हर प्रणाली को प्रभावित करता है- या उपयोगकर्ता-परिभाषित। समारोह जो ढेर स्मृति आवंटित करता है।
शुरुआती यूनिक्स डिजाइनर बहुत चतुर लोग थे, और कई कारण हैं कि क्यों मूल रूप free
से बेहतर है mfree
मूल रूप से मुझे लगता है कि प्रश्न का उत्तर यह है कि उन्होंने इस पर ध्यान दिया और तदनुसार अपनी प्रणाली तैयार की। मुझे संदेह है कि जिस समय उन्होंने यह निर्णय लिया उस समय उनके सिर के अंदर क्या चल रहा था, इसका कोई प्रत्यक्ष रिकॉर्ड आपको मिल जाएगा। लेकिन हम कल्पना कर सकते हैं।
प्रीटेंड करें कि आप सी में एप्लिकेशन लिख रहे हैं V6 यूनिक्स पर चलाने के लिए, इसके दो-तर्क के साथ mfree
। आपने अब तक ठीक प्रबंधित किया है, लेकिन इन पॉइंटर आकारों का ध्यान रखना अधिक परेशानी का कारण बन रहा है क्योंकि आपके कार्यक्रम अधिक महत्वाकांक्षी हो जाते हैं और ढेर आवंटित चर का अधिक से अधिक उपयोग करने की आवश्यकता होती है। लेकिन फिर आपके पास एक शानदार विचार है: size_t
हर समय इन चारों ओर नकल करने के बजाय , आप बस कुछ उपयोगिता कार्य लिख सकते हैं, जो आवंटित स्मृति के अंदर सीधे आकार को रोकते हैं:
void *my_alloc(size_t size) {
void *block = malloc(sizeof(size) + size);
*(size_t *)block = size;
return (void *) ((size_t *)block + 1);
}
void my_free(void *block) {
block = (size_t *)block - 1;
mfree(block, *(size_t *)block);
}
और इन नए कार्यों का उपयोग करके आप जितना अधिक कोड लिखेंगे, वे उतने ही भयानक लगेंगे। न केवल वे आपके कोड को लिखना आसान बनाते हैं, वे आपके कोड को भी तेज़ बनाते हैं - दो चीजें जो अक्सर एक साथ नहीं चलती हैं! इससे पहले कि आप इन स्थानों पर size_t
चारों ओर से गुजर रहे थे , जिसमें नकल के लिए सीपीयू ओवरहेड जोड़ा गया था, और इसका मतलब था कि आपको अधिक बार रजिस्टर करना होगा (अतिरिक्त कार्य तर्कों के लिए) और व्यर्थ मेमोरी (क्योंकि नेस्टेड फ़ंक्शन कॉल अक्सर परिणाम देगा। size_t
विभिन्न स्टैक फ्रेम में संग्रहीत होने की कई प्रतियों में )। अपने नए सिस्टम में, आपको अभी भी मेमोरी को स्टोर करने के लिए खर्च करना होगाsize_t
, लेकिन केवल एक बार, और यह कहीं भी नकल नहीं करता है। ये छोटी क्षमता की तरह लग सकते हैं, लेकिन ध्यान रखें कि हम 256 केबी की रैम के साथ उच्च अंत मशीनों के बारे में बात कर रहे हैं।
यह आपको खुश करता है! तो आप दाढ़ी वाले पुरुषों के साथ अपनी शांत चाल साझा करते हैं जो अगले यूनिक्स रिलीज़ पर काम कर रहे हैं, लेकिन यह उन्हें खुश नहीं करता है, यह उन्हें दुखी करता है। आप देखते हैं, वे जैसे नए उपयोगिता कार्यों का एक गुच्छा जोड़ने की प्रक्रिया में थे strdup
, और उन्हें पता चलता है कि आपके शांत चाल का उपयोग करने वाले लोग अपने नए कार्यों का उपयोग नहीं कर पाएंगे, क्योंकि उनके नए कार्य सभी बोझिल सूचक + आकार का उपयोग करते हैं एपीआई। और फिर यह आपको दुखी भी करता है, क्योंकि आपको एहसास होता है कि आपको strdup(char *)
सिस्टम प्रोग्राम का उपयोग करने में सक्षम होने के बजाय आपके द्वारा लिखे गए प्रत्येक प्रोग्राम में अच्छे कार्य को फिर से लिखना होगा ।
लेकिन रुकें! यह 1977 है, और 5 साल के लिए पीछे की संगतता का आविष्कार नहीं किया जाएगा! और इसके अलावा, कोई भी गंभीर वास्तव में इस अस्पष्ट "यूनिक्स" चीज़ का उपयोग अपने ऑफ-कलर नाम के साथ नहीं करता है। K & R का पहला संस्करण अब प्रकाशक के पास है, लेकिन यह कोई समस्या नहीं है - यह पहले पृष्ठ पर सही कहता है कि "C वर्ण संरेखण जैसी समग्र वस्तुओं से सीधे निपटने के लिए कोई संचालन प्रदान नहीं करता है ... कोई ढेर नहीं है ... "। इतिहास में इस बिंदु पर, string.h
और malloc
विक्रेता एक्सटेंशन (!) हैं। तो, दाढ़ी वाले आदमी # 1 का सुझाव देता है, हम उन्हें पसंद कर सकते हैं लेकिन हम उन्हें पसंद करते हैं; क्यों हम सिर्फ अपने मुश्किल आवंटनकर्ता को आधिकारिक आवंटनकर्ता घोषित नहीं करते हैं ?
कुछ दिनों बाद, दाढ़ी वाले आदमी # 2 नए एपीआई को देखता है और कहता है कि रुको, यह पहले से बेहतर है, लेकिन यह अभी भी आकार के भंडारण के प्रति आवंटन का एक पूरा शब्द खर्च कर रहा है। वह इसे ईश निंदा की अगली बात मानते हैं। हर कोई उसे देखता है जैसे वह पागल है, क्योंकि आप और क्या कर सकते हैं? उस रात वह देर तक रहता है और एक नए आवंटनकर्ता का आविष्कार करता है जो आकार को बिल्कुल भी संग्रहीत नहीं करता है, लेकिन इसके बजाय सूचक मान पर काले जादू बिटशफ्ट्स का प्रदर्शन करके मक्खी पर इसे सीमित करता है, और नए एपीआई को जगह में रखते हुए इसे स्वैप करता है। नए एपीआई का मतलब है कि कोई भी स्विच को नोटिस नहीं करता है, लेकिन वे नोटिस करते हैं कि अगली सुबह कंपाइलर 10% कम रैम का उपयोग करता है।
और अब हर कोई खुश है: आपको अपना आसान लिखने और तेज कोड प्राप्त करने के लिए, दाढ़ी वाले आदमी # 1 को एक अच्छा सरल लिखना है strdup
जो लोग वास्तव में उपयोग करेंगे, और दाढ़ी वाले आदमी # 2 - विश्वास है कि उसने अपने बिट को बनाए रखा है - - quines के साथ खिलवाड़ करने के लिए वापस चला जाता है । इसे भेज दो!
या कम से कम, कि यह कैसे हो सकता है।