2009-11-30 17 views
11

यूनियन के विरोध में बस एक शून्य * का उपयोग करने के बीच अंतर क्या होगा? उदाहरण:यूनियन बनाम शून्य सूचक

struct my_struct { 
    short datatype; 
    void *data; 
} 

struct my_struct { 
    short datatype; 
    union { 
     char* c; 
     int* i; 
     long* l; 
    }; 
}; 

उन दोनों सटीक इसी कार्य को पूरा करने के लिए इस्तेमाल किया जा सकता है, यह बेहतर यूनियन या शून्य उपयोग करने के लिए * यद्यपि है?

+5

क्लिच क्षमा करें, लेकिन आकार मायने रखता है :) –

+2

@ टिंकर्टिम और बाधाएं हैं कि वे एक ही आकार के होंगे। 32 बिट इंटेल सिस्टम पर, दोनों 4 बाइट लेंगे। तकनीकी रूप से, शून्य * बड़ा हो सकता है, लेकिन यह लगभग कभी नहीं होगा (जब तक कि डबल * बड़ा न हो ...) – Mikeage

+0

क्या सभी पॉइंटर्स समान आकार के डेटा के प्रकार के बावजूद समान आकार नहीं हैं? –

उत्तर

16

मेरे पास हमारी लाइब्रेरी में बिल्कुल मामला था। हमारे पास एक सामान्य स्ट्रिंग मैपिंग मॉड्यूल था जो इंडेक्स, 8, 16 या 32 बिट (ऐतिहासिक कारणों से) के लिए विभिन्न आकारों का उपयोग कर सकता था। तो कोड ऐसे ही कोड से भरा था:

if(map->idxSiz == 1) 
    return ((BYTE *)map->idx)[Pos] = ...whatever 
else 
    if(map->idxSiz == 2) 
    return ((WORD *)map->idx)[Pos] = ...whatever 
    else 
    return ((LONG *)map->idx)[Pos] = ...whatever 

वहाँ इस तरह लाइनों के 100 थे। पहले चरण में मैंने इसे एक संघ बदल दिया और मैंने पाया कि यह अधिक पठनीय था।

switch(map->idxSiz) { 
    case 1: return map->idx.u8[Pos] = ...whatever 
    case 2: return map->idx.u16[Pos] = ...whatever 
    case 3: return map->idx.u32[Pos] = ...whatever 
} 

यह मैं का विज्ञापन बेहतर क्या हो रहा था देखने के लिए और मैं तो दूर करने के लिए पूरी तरह से idxSiz केवल 32 बिट का उपयोग कर अनुक्रमित वेरिएंट तय कर सकता है। लेकिन कोड को और अधिक पठनीय होने के बाद ही यह संभव था। पीएस: यह हमारी परियोजना का केवल एक मामूली हिस्सा था जो कि अब मौजूद नहीं होने वाले लोगों द्वारा लिखे गए कोड की लगभग 100 हजार लाइन कोड है। इसलिए कोड में परिवर्तन धीरे-धीरे होते हैं ताकि अनुप्रयोगों को तोड़ न सके।

निष्कर्ष: यहां तक ​​कि यदि लोगों को यूनियन संस्करण में कम उपयोग किया जाता है, तो भी मैं इसे पसंद करता हूं क्योंकि यह कोड को पढ़ने के लिए बहुत हल्का कर सकता है। बड़ी परियोजनाओं पर, कोड को और अधिक पठनीय बनाना बेहद जरूरी है, भले ही यह स्वयं है जो इसे बाद में पढ़ेगा।

संपादित: टिप्पणियाँ कोड फ़ॉर्मेट नहीं करते के रूप में, टिप्पणी की:

स्विच करने के लिए बदलने से पहले आया

switch(this->IdxSiz) { 
    case 2: ((uint16_t*)this->iSort)[Pos-1] = (uint16_t)this->header.nUz; break; 
    case 4: ((uint32_t*)this->iSort)[Pos-1] = this->header.nUz; break; 
} 

में बदल गया था (यह अब असली कोड के रूप में यह किया गया है)

switch(this->IdxSiz) { 
    case 2: this->iSort.u16[Pos-1] = this->header.nUz; break; 
    case 4: this->iSort.u32[Pos-1] = this->header.nUz; break; 
} 

मुझे कोड में किए गए सभी सौंदर्यीकरण को जोड़ना नहीं चाहिए था और केवल उस चरण को दिखाया जाना चाहिए था।लेकिन मैंने अपना उत्तर घर से पोस्ट किया जहां मुझे कोड

+5

मुझे लगता है कि पठनीयता घोंसला वाले ifs के बजाय स्विच का उपयोग करने से आता है। – swegi

+0

स्विच में परिवर्तन पहले आया था (यह अब वास्तविक कोड था) स्विच (यह-> IdxSiz) { केस 2: ((uint16_t *) यह-> iSort) [Pos-1] = (uint16_t) इस-> header.nUz; टूटना; केस 4: ((uint32_t *) यह-> iSort) [Pos-1] = this-> header.nUz; टूटना; } स्विच (यह-> IdxSiz) { केस 2: यह-> iSort.u16 [Pos-1] = this-> header.nUz; टूटना; केस 4: यह-> iSort.u32 [Pos-1] = this-> header.nUz; टूटना; } मुझे कोड में किए गए सभी सौंदर्यीकरण को जोड़ना नहीं चाहिए था और केवल उस चरण को दिखाया जाना चाहिए था। लेकिन मैंने अपना उत्तर घर से पोस्ट किया जहां मुझे कोड तक पहुंच नहीं थी। –

11

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

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

+0

मेमोरी खपत की बात आने पर इरादा और उपयोग दो अलग-अलग चीजें हैं। एक संघ अपने सबसे बड़े सदस्य के रूप में बड़ा है। –

+0

लेकिन इस मामले में .. संघ में सभी सदस्य पॉइंटर्स हैं .. तो यह एक शून्य * सही के समान आकार होना चाहिए? – FatDaemon

+1

spec में कुछ भी पॉइंटर्स को एक ही आकार के लिए आवश्यक नहीं है, लेकिन शून्य * किसी भी अन्य सूचक को संभालने के लिए पर्याप्त होना चाहिए। उस ने कहा, व्यावहारिक रूप से, पॉइंटर्स आम तौर पर एक ही आकार – Mikeage

2

हालांकि संघ का उपयोग आजकल आम नहीं है, क्योंकि संघ आपके उपयोग परिदृश्य के लिए अधिक निश्चित है, सूट अच्छी तरह से। पहले कोड नमूने में यह डेटा की सामग्री को समझ में नहीं आता है।

5

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

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

+0

हां, लेकिन यदि आपको डेटा प्रकार पता नहीं है तो आपके पास कोई विकल्प नहीं है, इसलिए "कौन सा?" प्रश्न व्यर्थ होगा। –

2

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

+0

यह कोई सुरक्षा प्रदान नहीं करता है; यदि आप char * स्टोर करते हैं और इसे int * के रूप में एक्सेस करते हैं, तो कंपाइलर पूरी तरह से खुश होगा, हालांकि अगर यह गठबंधन नहीं है, तो आपको समस्याएं होगी ... – Mikeage

+0

ध्यान दें कि मैंने सुरक्षा का "थोड़ा" कहा है। सच्चाई यह है कि सी आपको अपनी इच्छित गलती करने देगा। –

+0

वह बिट कौन सा है? आपको केवल int * char * या इसके विपरीत डालने के लिए मजबूर करना है? – Mikeage

2

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

मुझे लगता है कि अंत में दूसरे पर किसी भी तरह का कोई फायदा नहीं है।

2

यह आपके उदाहरण में थोड़ा अस्पष्ट है, क्योंकि आप पॉइंटर्स और इसलिए संकेत का उपयोग कर रहे हैं। लेकिन union निश्चित रूप से इसके फायदे हैं।

कल्पना कीजिए:

struct my_struct { 
    short datatype; 
    union { 
     char c; 
     int i; 
     long l; 
    }; 
}; 

अब आप के बारे में जहां मूल्य भाग के लिए आवंटन से आता है चिंता करने की जरूरत नहीं है। कोई अलग malloc() या ऐसा कुछ भी नहीं। और आपको लगता है कि ->c, ->i तक पहुंच सकता है, और ->l थोड़ा तेज़ है। (हालांकि इनमें से बहुत से उपयोग होने पर यह केवल एक फर्क पड़ सकता है।)

+2

शायद वे पॉइंटर्स एक सरणी को इंगित करें ...? – Mikeage

+0

@ माइकेज - फिर, यदि सरणी का एक निश्चित अधिकतम आकार है और वह आकार आपकी संरचना में डालने के लिए स्वीकार्य है, तो भी मैं एक संघ का उपयोग करने के लिए लुभाना चाहता हूं, जिसमें पॉइंटर्स नहीं हैं लेकिन सरणी शामिल हैं। अन्यथा मैं शून्य पॉइंटर्स का उपयोग करूंगा। चूंकि उदाहरण में आकार के सदस्य को शामिल नहीं किया गया था, इसलिए मुझे लगता है कि वह जो भी सोच रहा था वह नहीं था। – asveikau

6

पॉइंटर्स की बजाय वास्तविक वस्तुओं को रखने के लिए संघ का उपयोग करना अधिक आम है।

मुझे लगता है कि मैं जिन सी सी डेवलपर्स का सम्मान करता हूं वे अलग-अलग पॉइंटर्स को एक साथ जोड़ना नहीं चाहते हैं; यदि एक सामान्य उद्देश्य सूचक की आवश्यकता है, तो बस void * का उपयोग करके निश्चित रूप से "सी रास्ता" है। भाषा आपको जानबूझकर चीजों के प्रकार के उपनाम की अनुमति देने के लिए बहुत सारी सुरक्षा प्रदान करती है; इस सुविधा के लिए हमने जो भुगतान किया है, उस पर विचार करते हुए हम कोड को सरल बनाते समय इसका उपयोग भी कर सकते हैं। यही कारण है कि सख्त टाइपिंग से बच निकलती रही है।

1

यह वास्तव में उस समस्या पर निर्भर करता है जिसे आप हल करने का प्रयास कर रहे हैं। उस संदर्भ के बिना मूल्यांकन करना वास्तव में असंभव है जो बेहतर होगा।

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

2

पर कोई पहुंच नहीं थी, यदि आप -फ्रेम-एलियासिंग (जीसीसी) या अन्य कंपाइलर्स पर समान विकल्प के साथ अपना कोड बनाते हैं, तो आपको बहुत सावधान रहना होगा कि आप कैसे करते हैं आपका कास्टिंग आप जितना चाहें उतना पॉइंटर डाल सकते हैं, लेकिन जब आप इसे अस्वीकार करते हैं, तो पॉइंटर प्रकार जिसे आप dereference के लिए उपयोग करते हैं, मूल प्रकार (कुछ अपवादों के साथ) से मेल खाना चाहिए। उदाहरण के लिए आप नहीं की तरह कुछ कर सकते हैं: अगर यह 0 (माना) 0xFFFF या 0xFFFF0000 के बजाय के रूप में आप जब अनुकूलन के साथ निर्माण की अपेक्षा कर सकते प्रिंट

 
void foo(void * p) 
{ 
    short * pSubSetOfInt = (short *)p ; 
    *pSubSetOfInt = 0xFFFF ; 
} 

void goo() 
{ 
    int intValue = 0 ; 

    foo(&intValue) ; 

    printf("0x%X\n", intValue) ; 
} 

आश्चर्य नहीं है। इस कोड को काम करने का एक तरीका यूनियन का उपयोग करके वही काम करना है, और कोड भी समझना आसान होगा।

1

यूनियन सबसे बड़े सदस्य के लिए पर्याप्त जगह सुरक्षित रखता है, उन्हें समान नहीं होना चाहिए, क्योंकि शून्य * का एक निश्चित आकार है, जबकि यूनियन का मनमाना आकार के लिए उपयोग किया जा सकता है।

#include <stdio.h> 
#include <stdlib.h> 

struct m1 { 
    union { 
    char c[100]; 
    }; 
}; 

struct m2 { 
    void * c; 
}; 


int 
main() 
{ 
printf("sizeof m1 is %d ",sizeof(struct m1)); 
printf("sizeof m2 is %d",sizeof(struct m2)); 
exit(EXIT_SUCCESS); 
} 

आउटपुट: sizeof एम 1 100 sizeof m2 है है 4

संपादित: यह मानकर कि आप केवल शून्य * के रूप में एक ही आकार के संकेत का उपयोग मैं, लगता है संघ बेहतर है के रूप में आप प्राप्त करेंगे एक पूर्णांक सूचक, आदि के साथ .c सेट करने का प्रयास करते समय त्रुटि का पता लगाने में थोड़ा सा। शून्य *, जब तक कि आप स्वयं को आवंटित नहीं कर रहे हैं, निश्चित रूप से तेज़ और गंदा है, बेहतर या बदतर के लिए।

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