c - क्या मैं मॉलोक का नतीजा डालता हूं?
malloc casting (18)
इस सवाल में , किसी ने comment में सुझाव दिया कि मुझे malloc
का नतीजा नहीं देना चाहिए, यानी
int *sieve = malloc(sizeof(int) * length);
बजाय:
int *sieve = (int *) malloc(sizeof(int) * length);
यह मामला क्यों होगा?
नहीं, आप का परिणाम नहीं डाला है malloc()
।
आम तौर पर, आप को या से नहीं डाला जाता हैvoid *
।
ऐसा करने के लिए दिया गया एक सामान्य कारण यह है कि विफलता पर #include <stdlib.h>
ध्यान नहीं दिया जा सकता है। अब यह अब तक कोई मुद्दा नहीं है क्योंकि सी 99 ने निहित कार्य घोषणाओं को गैरकानूनी बना दिया है , इसलिए यदि आपका कंपाइलर कम से कम सी 99 के अनुरूप है, तो आपको नैदानिक संदेश मिलेगा।
लेकिन अनावश्यक सूचक का परिचय नहीं देने के लिए एक बहुत मजबूत कारण है :
सी में, एक सूचक कास्ट लगभग हमेशा एक त्रुटि है । यह निम्नलिखित नियमों के कारण है ( एन 1570 में §6.5 पी 7 , सी 11 के लिए नवीनतम मसौदा):
एक ऑब्जेक्ट में इसके संग्रहीत मूल्य को केवल एक लेल्यू अभिव्यक्ति द्वारा एक्सेस किया जाएगा जिसमें निम्न प्रकारों में से
एक है : - ऑब्जेक्ट के प्रभावी प्रकार के साथ संगत एक प्रकार,
- ऑब्जेक्ट के प्रभावी प्रकार के साथ संगत प्रकार का एक योग्य संस्करण,
- एक प्रकार जो ऑब्जेक्ट के प्रभावी प्रकार से संबंधित हस्ताक्षरित या हस्ताक्षरित प्रकार है,
- एक प्रकार जो ऑब्जेक्ट के प्रभावी प्रकार के योग्य संस्करण के अनुरूप हस्ताक्षरित या हस्ताक्षरित प्रकार है,
- कुल या संघ प्रकार जिसमें एक शामिल है इसके सदस्यों के बीच उपर्युक्त प्रकारों (जिसमें, संक्षेप में, एक उपनिवेश या निहित संघ का सदस्य), या
- एक चरित्र प्रकार।
इसे सख्त एलियासिंग नियम भी कहा जाता है । तो निम्न कोड अपरिभाषित व्यवहार है :
long x = 5;
double *p = (double *)&x;
double y = *p;
और, कभी-कभी आश्चर्य की बात है, निम्नलिखित भी है:
struct foo { int x; };
struct bar { int x; int y; };
struct bar b = { 1, 2};
struct foo *p = (struct foo *)&b;
int z = p->x;
कभी कभी, आप करते संकेत कास्ट करने के लिए की जरूरत है, लेकिन यह देखते हुए सख्त अलियासिंग नियम , आप इसके साथ बहुत सावधान रहना होगा। इसलिए, आपके कोड में पॉइंटर कास्ट होने की कोई भी घटना एक ऐसी जगह है जहां आपको इसकी वैधता के लिए दोबारा जांच करनी होगी । इसलिए, आप कभी भी एक अनावश्यक पॉइंटर कास्ट नहीं लिखते हैं।
tl; डॉ
संक्षेप में: क्योंकि सी में, पॉइंटर कास्ट की किसी भी घटना को कोड के लिए लाल झंडा उठाना चाहिए, विशेष ध्यान देने की आवश्यकता है, आपको अनावश्यक पॉइंटर कास्ट कभी नहीं लिखना चाहिए ।
साइड नोट्स:
ऐसे मामले हैं जहां आपको वास्तव में एक कलाकार की आवश्यकता होती है
void *
, उदाहरण के लिए यदि आप एक सूचक मुद्रित करना चाहते हैं:int x = 5; printf("%p\n", (void *)&x);
कलाकार यहां जरूरी है, क्योंकि
printf()
एक विविध कार्य है, इसलिए निहित रूपांतरण काम नहीं करते हैं।सी ++ में, स्थिति अलग है। व्युत्पन्न कक्षाओं की वस्तुओं से निपटने के दौरान पॉइंटर प्रकार कास्टिंग कुछ हद तक आम (और सही) होता है। इसलिए, यह समझ में आता है कि सी ++ में, करने के लिए और से रूपांतरण
void *
है नहीं निहित। सी ++ कास्टिंग के विभिन्न स्वादों का एक पूरा सेट है।
जैसा कि अन्य ने कहा है, सी के लिए इसकी आवश्यकता नहीं है, लेकिन सी ++ के लिए।
कलाकारों को शामिल करने से सी प्रोग्राम या फ़ंक्शन को C ++ के रूप में संकलित करने की अनुमति मिल सकती है।
सी में यह अनावश्यक है, क्योंकि शून्य * किसी अन्य सूचक प्रकार के लिए स्वचालित रूप से और सुरक्षित रूप से प्रचारित होता है।
लेकिन अगर आप तब डालेंगे, तो अगर आप stdlib.h को शामिल करना भूल गए हैं तो यह एक त्रुटि छिपा सकता है । यह दुर्घटनाओं का कारण बन सकता है (या, बदतर, बाद में कोड के कुछ अलग-अलग हिस्सों में दुर्घटना का कारण नहीं बनता)।
क्योंकि stdlib.h में malloc के लिए प्रोटोटाइप पाया जाता है। मॉलोक के लिए प्रोटोटाइप की अनुपस्थिति में, मानक के लिए आवश्यक है कि सी कंपाइलर मानता है कि मॉलोक एक int देता है। यदि कोई कलाकार नहीं है, तो संकेतक जारी किया जाता है जब यह पूर्णांक सूचक को सौंपा जाता है; हालांकि, कलाकारों के साथ, यह चेतावनी उत्पन्न नहीं होती है, एक बग छुपाती है।
आप मॉलोक का नतीजा नहीं डालते हैं, क्योंकि ऐसा करने से आपके कोड में व्यर्थ अव्यवस्था बढ़ जाती है।
लोगों ने मॉलोक का नतीजा डालने का सबसे आम कारण यह है कि वे इस बारे में अनिश्चित हैं कि सी भाषा कैसे काम करती है। यह एक चेतावनी संकेत है: यदि आप नहीं जानते कि कोई विशेष भाषा तंत्र कैसे काम करता है, तो अनुमान न लें। इसे देखो या स्टैक ओवरफ़्लो पर पूछें।
कुछ टिप्पणियां:
एक शून्य सूचक को किसी भी अन्य सूचक प्रकार से स्पष्ट रूप से बिना किसी कास्ट (सी 11 6.3.2.3 और 6.5.16.1) में परिवर्तित किया जा सकता है।
हालांकि C ++
void*
और अन्य सूचक प्रकार के बीच एक अंतर्निहित कास्ट की अनुमति नहीं देगा। तो सी ++ में, कास्ट सही होता। लेकिन यदि आप सी ++ में प्रोग्राम करते हैं, तो आपकोnew
और मॉलोक () का उपयोग करना चाहिए। और आपको कभी भी C ++ कंपाइलर का उपयोग करके सी कोड संकलित नहीं करना चाहिए।यदि आपको एक ही स्रोत कोड के साथ सी और सी ++ दोनों का समर्थन करने की आवश्यकता है, तो मतभेदों को चिह्नित करने के लिए कंपाइलर स्विच का उपयोग करें। एक ही कोड के साथ दोनों भाषा मानकों को पूरा करने का प्रयास न करें, क्योंकि वे संगत नहीं हैं।
यदि कोई सी कंपाइलर फ़ंक्शन नहीं ढूंढ पा रहा है क्योंकि आप हेडर को शामिल करना भूल गए हैं, तो आपको इसके बारे में एक कंपाइलर / लिंकर त्रुटि मिलेगी। इसलिए यदि आप
<stdlib.h>
को शामिल करना भूल गए हैं, तो यह कोई बड़ी बात नहीं है, तो आप अपना प्रोग्राम नहीं बना पाएंगे।प्राचीन कंपाइलर्स पर जो मानक के संस्करण का पालन करते हैं जो कि 25 वर्ष से अधिक पुराना है,
<stdlib.h>
को शामिल करना<stdlib.h>
खतरनाक व्यवहार में होगा। क्योंकि उस प्राचीन मानक में, एक दृश्य प्रोटोटाइप के बिना कार्यों ने पूरी तरह से वापसी प्रकार कोint
परिवर्तित कर दिया। मॉलोक से परिणाम कास्टिंग स्पष्ट रूप से इस बग को छिपाएगा।लेकिन यह वास्तव में एक गैर मुद्दा है। आप 25 साल के कंप्यूटर का उपयोग नहीं कर रहे हैं, तो आप 25 साल के कंपाइलर का उपयोग क्यों करेंगे?
एक शून्य सूचक एक सामान्य सूचक है और सी शून्य सूचक प्रकार से अन्य प्रकारों में अंतर्निहित रूपांतरण का समर्थन करता है, इसलिए इसे स्पष्ट रूप से टाइपकास्टिंग की आवश्यकता नहीं है।
हालांकि, यदि आप एक ही कोड को सी ++ प्लेटफॉर्म पर पूरी तरह से संगत करना चाहते हैं, जो अंतर्निहित रूपांतरण का समर्थन नहीं करता है, तो आपको टाइपकास्टिंग करना होगा, इसलिए यह सब उपयोगिता पर निर्भर करता है।
जैसा कि अन्य ने कहा है, सी के लिए इसकी आवश्यकता नहीं है, लेकिन सी ++ के लिए। यदि आपको लगता है कि आप अपने सी कोड को सी ++ कंपाइलर के साथ संकलित करने जा रहे हैं, जिसके कारण कभी भी आप मैक्रो का उपयोग कर सकते हैं, जैसे:
#ifdef __cplusplus
# define NEW(type, count) ((type *)calloc(count, sizeof(type)))
#else
# define NEW(type, count) (calloc(count, sizeof(type)))
#endif
इस तरह आप इसे अभी भी एक बहुत ही कॉम्पैक्ट तरीके से लिख सकते हैं:
int *sieve = NEW(int, 1);
और यह सी और सी ++ के लिए संकलित होगा।
बस अपने अनुभव को जोड़कर, कंप्यूटर इंजीनियरिंग का अध्ययन करते हुए मैं देखता हूं कि मैंने दो या तीन प्रोफेसरों को सी में लिखते हुए हमेशा मॉलोक डाला है, हालांकि मैंने जो पूछा (एक सीवी और सी की समझ के साथ) ने मुझे बताया कि यह बिल्कुल अनावश्यक है लेकिन केवल बिल्कुल विशिष्ट होने के लिए प्रयोग किया जाता है, और छात्रों को बिल्कुल विशिष्ट होने की मानसिकता में प्राप्त करने के लिए उपयोग किया जाता है। अनिवार्य रूप से कास्टिंग यह कैसे काम करता है में कुछ भी नहीं बदलेगा, यह वही करता है जो यह कहता है, स्मृति आवंटित करता है, और कास्टिंग इसे प्रभावित नहीं करता है, आपको वही स्मृति मिलती है, और यहां तक कि अगर आप गलती से इसे किसी अन्य चीज़ पर डाल देते हैं (और किसी भी तरह संकलक से बचें त्रुटियां) सी इसे उसी तरह एक्सेस करेगा।
संपादित करें: कास्टिंग एक निश्चित बिंदु है। जब आप सरणी नोटेशन का उपयोग करते हैं, तो उत्पन्न कोड को पता होना चाहिए कि अगले तत्व की शुरुआत तक पहुंचने के लिए कितनी मेमोरी जगहों को अग्रिम करना है, यह कास्टिंग के माध्यम से हासिल किया जाता है। इस तरह आप जानते हैं कि एक डबल के लिए आप 8 बाइट आगे जाते हैं जबकि एक int के लिए आप 4 जाते हैं, और इसी तरह। इस प्रकार इसका कोई प्रभाव नहीं पड़ता है यदि आप पॉइंटर नोटेशन का उपयोग करते हैं, सरणी नोटेशन में यह आवश्यक हो जाता है।
यह प्रोग्रामिंग भाषा और संकलक पर निर्भर करता है। यदि आप सी में malloc
का उपयोग करते हैं तो इसे कास्ट करने की कोई आवश्यकता नहीं है, क्योंकि यह स्वचालित रूप से कास्ट टाइप करेगा, हालांकि यदि आप सी ++ का उपयोग करते हैं तो आपको कास्ट टाइप करना चाहिए क्योंकि malloc
एक void*
प्रकार वापस कर देगा।
लौटाया गया प्रकार शून्य * है, जिसे वांछनीय होने के लिए वांछित प्रकार के डेटा पॉइंटर पर डाला जा सकता है।
सी में आप निहित सूचक को किसी अन्य प्रकार के पॉइंटर में निहित रूप से परिवर्तित कर सकते हैं, इसलिए एक कास्ट आवश्यक नहीं है। किसी का उपयोग करके आकस्मिक पर्यवेक्षक को सुझाव दिया जा सकता है कि किसी कारण की आवश्यकता क्यों है, जो भ्रामक हो सकता है।
सी में आपको void*
से किसी भी अन्य (डेटा) सूचक में एक निहित रूपांतरण मिलता है।
जीएनयू सी लाइब्रेरी रेफरेंस मैनुअल यही कहता है:
आप
malloc
के परिणाम को किसी भी पॉइंटर वैरिएबल में बिना किसी कलाकार के स्टोर कर सकते हैं, क्योंकि जब आवश्यक हो तो आईएसओ सी स्वचालित रूप से प्रकार केvoid *
को किसी अन्य प्रकार के पॉइंटर में परिवर्तित कर देता है। लेकिन असाइनमेंट ऑपरेटर के अलावा संदर्भों में कलाकारों को जरूरी है या यदि आप चाहते हैं कि आपका कोड पारंपरिक सी में चलाना चाहें।
और वास्तव में आईएसओ सी 11 मानक (पी 347) ऐसा कहता है:
आवंटन सफल होने पर पॉइंटर वापस लौटाया जाता है ताकि इसे किसी भी प्रकार के ऑब्जेक्ट को मौलिक संरेखण आवश्यकता के साथ एक सूचक को असाइन किया जा सके और उसके बाद आवंटित स्थान में ऐसी ऑब्जेक्ट या ऐसी ऑब्जेक्ट्स की सरणी तक पहुंचने के लिए उपयोग किया जा सके (जब तक कि अंतरिक्ष स्पष्ट रूप से हटा दिया गया है)
कास्टिंग के लिए लाभ
कलाकारों को शामिल करने से सी प्रोग्राम या फ़ंक्शन को C ++ के रूप में संकलित करने की अनुमति मिल सकती है।
कास्ट मॉलोक के पूर्व -198 संस्करणों के लिए अनुमति देता है जो मूल रूप से एक char * लौटाता है।
कास्टिंग डेवलपर को टाइप आकार में असंगतता की पहचान करने में मदद कर सकता है, गंतव्य सूचक प्रकार बदलना चाहिए, विशेष रूप से यदि सूचक को मॉलोक () कॉल से दूर घोषित किया जाता है (हालांकि आधुनिक कंपाइलर्स और स्थिर विश्लेषक कलाकारों के बिना इस तरह के व्यवहार पर चेतावनी दे सकते हैं)।
कास्टिंग करने के नुकसान
एएनएसआई सी मानक के तहत, कास्ट अनावश्यक है।
कास्ट जोड़ना हेडर stdlib.h को शामिल करने में मास्क विफलता हो सकता है, जिसमें मॉलोक के लिए प्रोटोटाइप पाया जाता है। मॉलोक के लिए प्रोटोटाइप की अनुपस्थिति में, मानक की आवश्यकता है कि सी कंपाइलर मान लें कि मॉलोक एक int देता है। यदि कोई कलाकार नहीं है, तो संकेतक जारी किया जाता है जब यह पूर्णांक सूचक को सौंपा जाता है; हालांकि, कलाकारों के साथ, यह चेतावनी उत्पन्न नहीं होती है, एक बग छुपाती है। कुछ आर्किटेक्चर और डेटा मॉडल (जैसे कि 64-बिट सिस्टम पर एलपी 64, जहां लंबे और पॉइंटर्स 64-बिट और int 32-बिट हैं) पर, यह त्रुटि वास्तव में अपरिभाषित व्यवहार में परिणाम दे सकती है, क्योंकि निस्संदेह घोषित मॉलोक 32- बिट मान जबकि वास्तव में परिभाषित फ़ंक्शन 64-बिट मान देता है। कॉलिंग सम्मेलनों और मेमोरी लेआउट के आधार पर, इसका परिणाम स्टैक स्मैशिंग हो सकता है। इस मुद्दे को आधुनिक कंपाइलरों में अनजान जाने की संभावना कम है, क्योंकि वे समान रूप से चेतावनियां उत्पन्न करते हैं कि एक अविकसित कार्य का उपयोग किया गया है, इसलिए एक चेतावनी अभी भी दिखाई देगी। उदाहरण के लिए, जीसीसी का डिफ़ॉल्ट व्यवहार एक चेतावनी दिखाना है जो कि अंतर्निहित कार्य की असंगत निहित घोषणा "पढ़ता है, भले ही कास्ट मौजूद है या नहीं।
यदि इसकी घोषणा में पॉइंटर का प्रकार बदला जाता है, तो किसी को भी उन सभी लाइनों को बदलने की आवश्यकता हो सकती है जहां मॉलोक कहा जाता है और कास्ट किया जाता है।
हालांकि कास्टिंग के बिना मॉलोक को पसंदीदा तरीका है और सबसे अनुभवी प्रोग्रामर इसे चुनते हैं , आपको जो भी मुद्दों के बारे में पता होना चाहिए, उसका उपयोग करना चाहिए।
यानी: यदि आपको सी प्रोग्राम को सी ++ के रूप में संकलित करने की आवश्यकता है (हालांकि वे अलग भाषा हैं) तो आपको कास्टिंग के साथ malloc
उपयोग करना चाहिए।
malloc
के परिणामों को डालना अनिवार्य नहीं है, क्योंकि यह void*
देता void*
, और एक void*
किसी भी डेटाटाइप को इंगित किया जा सकता है।
नहीं ; आप परिणाम नहीं डाले, क्योंकि:
- यह अनावश्यक है, क्योंकि
void *
इस मामले में किसी भी अन्य सूचक प्रकार के लिए स्वचालित रूप से और सुरक्षित रूप से प्रचारित है। - यह कोड को अव्यवस्था जोड़ता है, कोस्ट पढ़ने के लिए बहुत आसान नहीं है (विशेष रूप से यदि सूचक प्रकार लंबा है)।
- यह आपको खुद को दोहराता है, जो आम तौर पर खराब होता है।
- यदि आप
<stdlib.h>
को शामिल करना भूल गए हैं तो यह एक त्रुटि छिपा सकता है। यह दुर्घटनाओं का कारण बन सकता है (या, बदतर, बाद में कोड के कुछ अलग-अलग हिस्सों में दुर्घटना का कारण नहीं बनता)। विचार करें कि क्या होता है यदि पॉइंटर्स और पूर्णांक अलग-अलग आकार में होते हैं; तो आप कास्टिंग करके एक चेतावनी छुपा रहे हैं और आपके लौटे पते के बिट्स खो सकते हैं। नोट: सी 11 निहित कार्यों के रूप में सी से चले गए हैं, और यह बिंदु अब प्रासंगिक नहीं है क्योंकि कोई स्वचालित धारणा नहीं है कि अविकसित कार्यint
वापस आते हैं।
एक स्पष्टीकरण के रूप में, ध्यान दें कि मैंने कहा था "आप नहीं डाले", न कि "आपको कास्ट करने की आवश्यकता नहीं है "। मेरी राय में, यह कलाकारों को शामिल करने में विफलता है, भले ही आपको यह सही लगे। ऐसा करने के लिए कोई लाभ नहीं है, लेकिन संभावित जोखिमों का एक गुच्छा, और कास्ट समेत इंगित करता है कि आपको जोखिमों के बारे में पता नहीं है।
यह भी ध्यान दें, क्योंकि टिप्पणीकार बताते हैं कि ऊपर सी के बारे में उपरोक्त वार्ता सी ++ नहीं है। मैं सी और सी ++ में अलग-अलग भाषाओं के रूप में बहुत दृढ़ता से विश्वास करता हूं।
आगे जोड़ने के लिए, आपका कोड अनिवार्य रूप से प्रकार की जानकारी ( int
) को दोहराता है जो त्रुटियों का कारण बन सकता है। रिटर्न वैल्यू को स्टोर करने के लिए पॉइंटर का उपयोग करने के लिए बेहतर है, दोनों को एक साथ "लॉक" करने के लिए:
int *sieve = malloc(length * sizeof *sieve);
यह बढ़ती दृश्यता के लिए length
को आगे बढ़ाता है, और अनावश्यक कोष्ठक को sizeof
साथ छोड़ देता है; जब तर्क एक प्रकार का नाम होता है तो उन्हें केवल तभी आवश्यकता होती है। बहुत से लोग इस बारे में नहीं जानते (या अनदेखा करते हैं), जो उनके कोड को अधिक वर्बोज़ बनाता है। याद रखें: sizeof
एक समारोह नहीं है! :)
मोर्चे की length
बढ़ने के दौरान कुछ दुर्लभ मामलों में दृश्यता बढ़ सकती है , किसी को यह भी ध्यान देना चाहिए कि सामान्य मामले में, अभिव्यक्ति को लिखना बेहतर होना चाहिए:
int *sieve = malloc(sizeof *sieve * length);
पहले sizeof
बनाए रखने के बाद, इस मामले में, कम से कम size_t
गणित के साथ गुणा किया जाता है।
तुलना करें: malloc(sizeof *sieve * length * width)
बनाम malloc(length * width * sizeof *sieve)
दूसरा length * width
overflow हो सकता है जब width
और length
size_t
से छोटे प्रकार हैं।
एक शून्य सूचक एक सामान्य सूचक है और सी शून्य सूचक प्रकार से अन्य प्रकारों में अंतर्निहित रूपांतरण का समर्थन करता है, इसलिए इसे स्पष्ट रूप से टाइपकास्टिंग की आवश्यकता नहीं है।
हालांकि, यदि आप एक ही कोड को सी ++ प्लेटफॉर्म पर पूरी तरह से संगत करना चाहते हैं, जो अंतर्निहित रूपांतरण का समर्थन नहीं करता है, तो आपको टाइपकास्टिंग करना होगा, इसलिए यह सब उपयोगिता पर निर्भर करता है।
कास्टिंग केवल सी ++ के लिए नहीं है। यदि आप सी ++ कंपाइलर का उपयोग कर रहे हैं तो आप इसे सी कंपाइलर में बेहतर रूप से बदल सकते हैं।
मैं कास्ट करना पसंद करता हूं, लेकिन मैन्युअल रूप से नहीं। मेरा पसंदीदा ग्लिब से उपयोग g_new
और g_new0
मैक्रोज़ का उपयोग कर रहा है । यदि ग्लिब का उपयोग नहीं किया जाता है, तो मैं समान मैक्रोज़ जोड़ूंगा। वे मैक्रोज़ टाइप सुरक्षा की समझौता किए बिना कोड डुप्लिकेशन को कम करते हैं। यदि आपको गलत प्रकार मिलता है, तो आपको गैर-शून्य पॉइंटर्स के बीच एक निहित कास्ट मिल जाएगा, जो चेतावनी (सी ++ में त्रुटि) का कारण बनता है। यदि आप परिभाषित हेडर को शामिल करना भूल जाते हैं g_new
और g_new0
आपको एक त्रुटि मिल जाएगी। g_new
और g_new0
दोनों एक ही तर्क लेते हैं, इसके विपरीत, malloc
उससे कम तर्क लेते हैं calloc
। 0
शून्य-प्रारंभिक स्मृति प्राप्त करने के लिए बस जोड़ें । कोड को बिना किसी बदलाव के सी ++ कंपाइलर के साथ संकलित किया जा सकता है।
शून्य पॉइंटर के पीछे की अवधारणा यह है कि इसे किसी भी डेटा प्रकार में डाला जा सकता है, यही कारण है कि मॉलोक शून्य हो जाता है। इसके अलावा आपको स्वचालित टाइपकास्टिंग से अवगत होना चाहिए। तो सूचक को डालना अनिवार्य नहीं है हालांकि आपको यह करना होगा। यह कोड को साफ रखने में मदद करता है और डीबगिंग में मदद करता है