C और C ++ में यूनियनों का उद्देश्य


254

मैंने पहले आराम से यूनियनों का उपयोग किया है; जब मैंने इस पोस्ट को पढ़ा तो मुझे घबराहट हुई और मुझे पता चला कि यह कोड है

union ARGB
{
    uint32_t colour;

    struct componentsTag
    {
        uint8_t b;
        uint8_t g;
        uint8_t r;
        uint8_t a;
    } components;

} pixel;

pixel.colour = 0xff040201;  // ARGB::colour is the active member from now on

// somewhere down the line, without any edit to pixel

if(pixel.components.a)      // accessing the non-active member ARGB::components

वास्तव में अपरिभाषित व्यवहार है जिसे संघ के एक सदस्य से पढ़ने के अलावा अन्य ने हाल ही में अपरिभाषित व्यवहार के लिए लिखा है। यदि यह यूनियनों का इच्छित उपयोग नहीं है, तो क्या है? क्या कोई इसे विस्तार से बता सकता है?

अपडेट करें:

मैं कुछ बातें स्पष्ट करना चाहता था।

  • प्रश्न का उत्तर C और C ++ के लिए समान नहीं है; मेरे अज्ञानी युवा स्व ने इसे C और C ++ दोनों के रूप में टैग किया।
  • C ++ 11 के मानक के माध्यम से दस्त करने के बाद, मैं निर्णायक रूप से यह नहीं कह सकता कि यह एक गैर-सक्रिय संघ के सदस्य तक पहुँचने / निरीक्षण करने को अपरिभाषित / अनिर्दिष्ट / कार्यान्वयन-परिभाषित है। सभी मैं पा सकता था §9.5 / 1:

    यदि एक मानक-लेआउट संघ में कई मानक-लेआउट संरचनाएं होती हैं जो एक सामान्य प्रारंभिक अनुक्रम साझा करती हैं, और यदि इस मानक-लेआउट संघ प्रकार की एक वस्तु में मानक-लेआउट संरचना शामिल है, तो इसे किसी भी सामान्य प्रारंभिक अनुक्रम का निरीक्षण करने की अनुमति है मानक-लेआउट संरचना सदस्यों के। §9.2 / 19: दो मानक-लेआउट संरचनाएं एक सामान्य प्रारंभिक अनुक्रम साझा करती हैं यदि संबंधित सदस्यों में लेआउट-संगत प्रकार होते हैं और या तो सदस्य बिट-फ़ील्ड नहीं होते हैं या दोनों एक या अधिक प्रारंभिक अनुक्रम के लिए समान चौड़ाई वाले बिट-फ़ील्ड होते हैं सदस्य हैं।

  • जबकि C में, ( C99 TC3 - DR 283 आगे) ऐसा करने के लिए कानूनी है ( इसे ऊपर लाने के लिए पास्कल कूक का धन्यवाद )। हालाँकि, ऐसा करने का प्रयास अभी भी अपरिभाषित व्यवहार को जन्म दे सकता है , यदि पढ़ा हुआ मान अमान्य हो जाता है (तथाकथित "ट्रैप प्रतिनिधित्व") उस प्रकार के लिए जिसे इसके माध्यम से पढ़ा जाता है। अन्यथा, पढ़ा गया मान कार्यान्वयन है।
  • C89 / 90 ने इसे अनिर्दिष्ट व्यवहार (अनुलग्नक J) के तहत बुलाया और K & R की पुस्तक कहती है कि यह कार्यान्वयन परिभाषित है। K & R से उद्धरण:

    यह एक संघ का उद्देश्य है - एक एकल चर जो वैध रूप से कई प्रकारों में से किसी एक को पकड़ सकता है। [...] जब तक उपयोग सुसंगत है: जिस प्रकार को पुनर्प्राप्त किया जाना चाहिए वह प्रकार हाल ही में संग्रहीत किया गया है। यह प्रोग्रामर की ज़िम्मेदारी है कि वह इस बात पर नज़र रखे कि एक यूनियन में वर्तमान में किस प्रकार का संग्रह है; यदि कोई चीज़ एक प्रकार के रूप में संग्रहीत की जाती है और दूसरे के रूप में निकाली जाती है तो परिणाम कार्यान्वयन-निर्भर होते हैं।

  • स्ट्रॉस्ट्रुप की टीसी ++ पीएल (जोर मेरा) से निकालें

    डेटा की संगतता के लिए यूनियनों का उपयोग आवश्यक हो सकता है [...] कभी-कभी "टाइप रूपांतरण " के लिए दुरुपयोग किया जाता है

इन सबसे ऊपर, यह सवाल (जिसका शीर्षक मेरे पूछने के बाद से अपरिवर्तित रहता है) को यूनियनों के उद्देश्य को समझने के इरादे से पेश किया गया था और न कि मानक क्या अनुमति देता है जैसे कोड पुन: उपयोग के लिए विरासत का उपयोग करना, ज़ाहिर है, सी ++ मानक द्वारा अनुमत है, लेकिन यह C ++ भाषा फीचर के रूप में विरासत को पेश करने का उद्देश्य या मूल उद्देश्य नहीं था । यही कारण है कि एंड्री का जवाब स्वीकृत के रूप में जारी है।


11
बस कहा गया है, कंपाइलरों को एक संरचना में तत्वों के बीच पैडिंग डालने की अनुमति है। इस प्रकार, b, g, r,और aसन्निहित नहीं भी हो सकता है, और इस प्रकार ए के लेआउट से मेल नहीं खाता है uint32_t। यह एंडियन के उन मुद्दों के अतिरिक्त है जो दूसरों ने इंगित किए हैं।
थॉमस मैथ्यूज

8
यही कारण है कि आपको C और C ++ पर टैग नहीं लगाने चाहिए। उत्तर अलग-अलग हैं, लेकिन चूंकि उत्तरदाता यह भी नहीं बताते हैं कि वे किस टैग का उत्तर दे रहे हैं (क्या वे भी जानते हैं?), तो आपको बकवास लगता है।
पास्कल क्यूक

5
@downvoter समझाने के लिए धन्यवाद, मैं समझता हूं कि आप चाहते हैं कि मैं आपको जादुई रूप से अपनी
पकड़ समझूं

1
संघ के मूल इरादे के बारे में , यह ध्यान रखें कि C मानक कई वर्षों के बाद C यूनियनों को नियत करता है। यूनिक्स V7 पर एक त्वरित नज़र यूनियनों के माध्यम से कुछ प्रकार के रूपांतरण दिखाती है।
नंजलज

3
scouring C++11's standard I couldn't conclusively say that it calls out accessing/inspecting a non-active union member is undefined [...] All I could find was §9.5/1...वास्तव में? आप एक अपवाद नोट को उद्धृत करते हैं , पैराग्राफ की शुरुआत में मुख्य बिंदु सही नहीं है : "एक संघ में, गैर-स्थैतिक डेटा सदस्यों में से अधिकांश किसी भी समय सक्रिय हो सकता है, अर्थात, सबसे अधिक में से एक का मूल्य गैर-स्थैतिक डेटा सदस्यों को किसी भी समय एक संघ में संग्रहीत किया जा सकता है। " - और पी 4 से नीचे: "सामान्य तौर पर, किसी को संघ के सक्रिय सदस्य को बदलने के लिए स्पष्ट विध्वंसक कॉल और प्लेसमेंट नए ऑपरेटरों का उपयोग करना चाहिए "
अंडरस्कोर_ड

जवाबों:


407

यूनियनों का उद्देश्य बल्कि स्पष्ट है, लेकिन किसी कारण से लोग इसे अक्सर याद करते हैं।

संघ का उद्देश्य अलग-अलग समय पर विभिन्न वस्तुओं के भंडारण के लिए एक ही मेमोरी क्षेत्र का उपयोग करके मेमोरी को सहेजना हैबस।

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

ठीक यही संघ करता है। यदि आप जानते हैं कि आपके कार्यक्रम में कई ऑब्जेक्ट गैर-अतिव्यापी मूल्य-जीवनकाल के साथ मूल्य रखते हैं, तो आप इन वस्तुओं को एक संघ में "मर्ज" कर सकते हैं और इस प्रकार मेमोरी को बचा सकते हैं। जैसे होटल के कमरे में समय के प्रत्येक क्षण में एक "सक्रिय" किरायेदार होता है, वैसे ही एक संघ के पास कार्यक्रम समय के प्रत्येक क्षण में एक "सक्रिय" सदस्य होता है। केवल "सक्रिय" सदस्य को पढ़ा जा सकता है। अन्य सदस्य में लिखकर आप "सक्रिय" स्थिति को उस अन्य सदस्य पर स्विच करते हैं।

किसी कारण से, संघ के इस मूल उद्देश्य को "पूरी तरह से अलग" कुछ अलग से मिला: एक संघ के एक सदस्य को लिखना और फिर दूसरे सदस्य के माध्यम से इसका निरीक्षण करना। इस तरह की मेमोरी रीइंटरप्रिटेशन (उर्फ "टाइप पिंगिंग") यूनियनों का एक वैध उपयोग नहीं है। यह आमतौर पर अपरिभाषित व्यवहार की ओर जाता है जिसे C89 / 90 में कार्यान्वयन-परिभाषित व्यवहार के रूप में वर्णित किया गया है।

संपादित करें: टाइप पाइंटिंग के उद्देश्यों के लिए यूनियनों का उपयोग करना (यानी एक सदस्य को लिखना और फिर एक और पढ़ना) को C99 मानक (तकनीकी डीआर # 257 और डीआर # 283 देखें ) में से एक में एक अधिक विस्तृत परिभाषा दी गई थी । हालाँकि, ध्यान रखें कि औपचारिक रूप से यह आपको जाल प्रतिनिधित्व को पढ़ने का प्रयास करके अपरिभाषित व्यवहार में चलने से नहीं बचाता है।


37
+1 विस्तृत होने के लिए, एक सरल व्यावहारिक उदाहरण देते हुए और यूनियनों की विरासत के बारे में कहते हुए!
किंवदंतियों

6
इस जवाब के साथ मुझे जो समस्या है वह यह है कि मैंने देखा ज्यादातर OSes में हेडर फाइलें होती हैं जो यह सटीक काम करती हैं। उदाहरण के लिए मैंने इसे <time.h>विंडोज और यूनिक्स दोनों पर पुराने (64-बिट) संस्करणों में देखा है । इसे "मान्य नहीं" और "अपरिभाषित" के रूप में खारिज करना वास्तव में पर्याप्त नहीं है अगर मुझे इस सटीक तरीके से काम करने वाले कोड को समझने के लिए बुलाया जाए।
TED

31
@AndreyT "यह हाल ही में जब तक टाइपिंग के लिए यूनियनों का उपयोग करने के लिए कानूनी नहीं किया गया है": 2004 "बहुत हाल ही में" नहीं है, खासकर यह देखते हुए कि यह केवल C99 है जो शुरू में अनाड़ी शब्द था, यूनियनों के माध्यम से टाइप-डायनामिक अपरिभाषित बनाने के लिए प्रकट होता है। वास्तव में, टाइप-पिंगिंग हालांकि यूनियनों में C89 में कानूनी है, C11 में कानूनी है, और यह C99 में सभी के साथ कानूनी था, हालांकि समिति को गलत शब्दांकन को ठीक करने के लिए 2004 तक लिया गया था, और TC3 की बाद की रिलीज। open-std.org/jtc1/sc22/wg14/www/docs/dr_283.htm
पास्कल कूक

6
@ किंवदंतियां 2k प्रोग्रामिंग भाषा मानक द्वारा परिभाषित की जाती हैं। C99 मानक का तकनीकी कोरिगेंडम 3 स्पष्ट रूप से अपने फुटनोट 82 में टाइप-पिंगिंग की अनुमति देता है, जिसे मैं अपने लिए पढ़ने के लिए आमंत्रित करता हूं। यह टीवी नहीं है जहां रॉक स्टार का साक्षात्कार लिया जाता है और जलवायु परिवर्तन पर अपनी राय व्यक्त करते हैं। सी मानक क्या कहता है, इस पर स्ट्रॉस्ट्रुप की राय का शून्य प्रभाव है।
पास्कल क्यूक

6
@ legends2k " मुझे पता है कि किसी भी व्यक्ति की राय मायने नहीं रखती है और केवल मानक ही करता है " संकलक लेखकों की राय (बेहद खराब) भाषा "विनिर्देश" से बहुत अधिक मायने रखती है।
जिज्ञासु

38

आप यूनियनों का उपयोग निम्न प्रकार की संरचना बनाने के लिए कर सकते हैं, जिसमें एक क्षेत्र होता है जो हमें बताता है कि संघ के किस घटक का वास्तव में उपयोग किया जाता है:

struct VAROBJECT
{
    enum o_t { Int, Double, String } objectType;

    union
    {
        int intValue;
        double dblValue;
        char *strValue;
    } value;
} object;

मैं पूरी तरह से सहमत हूं, अपरिभाषित व्यवहार अराजकता में प्रवेश किए बिना, शायद यह उन यूनियनों का सबसे अच्छा इरादा व्यवहार है जिनके बारे में मैं सोच सकता हूं; लेकिन बेकार जगह नहीं है जब मैं बस का उपयोग कर रहा हूँ, कहते हैं intया char*वस्तु के 10 आइटम []; किस स्थिति में, मैं वास्तव में VAROBJECT के बजाय प्रत्येक डेटा प्रकार के लिए अलग संरचना की घोषणा कर सकता हूं? क्या यह अव्यवस्था को कम नहीं करेगा और कम स्थान का उपयोग करेगा?
लीजेंड्स 2

3
किंवदंतियों: कुछ मामलों में, आप बस ऐसा नहीं कर सकते। जब आप जावा में ऑब्जेक्ट का उपयोग करते हैं तो आप उन्हीं मामलों में C में VAROBJECT जैसी किसी चीज़ का उपयोग करते हैं।
Erich Kitzmueller

टैग की गई यूनियनों की डेटा संरचना केवल यूनियनों का एक वैध उपयोग लगती है, जैसा कि आप बताते हैं।
लीजेंड्स 2

मूल्यों का उपयोग कैसे करें का एक उदाहरण भी दें।
सिरो सेंटिल्ली 郝海东 iro i i

1
@CiroSantilli 新疆 改造 六四 事件++ ++ C ++ प्राइमर का एक उदाहरण , मदद कर सकता है। wandbox.org/permlink/cFSrXyG02vOSdBk2
रिक

34

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

यदि आप C ++ का उपयोग कर रहे हैं (आप दो टैग का उपयोग कर रहे हैं) और आप वास्तव में पोर्टेबिलिटी के बारे में परवाह करते हैं, तो आप बस संरचना का उपयोग कर सकते हैं और एक सेटर प्रदान कर सकते हैं जो uint32_tबिटमास्क संचालन के माध्यम से उचित रूप से और फ़ील्ड सेट करता है। एक फ़ंक्शन के साथ सी में भी ऐसा ही किया जा सकता है।

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


स्पष्ट और सरल उत्तर के लिए +1! मैं मानता हूं, पोर्टेबिलिटी के लिए, आपने 2 पैरा में जो तरीका दिया है, वह अच्छा है; लेकिन क्या मैं इस सवाल का इस्तेमाल कर सकता हूं, अगर मेरा कोड किसी एकल आर्किटेक्चर (प्रोटेक्शन की कीमत चुकाते हुए) से बंधा हुआ है, क्योंकि यह प्रत्येक पिक्सेल मूल्य के लिए 4 बाइट बचाता है और उस फ़ंक्शन को चलाने में कुछ समय बचता है ?
लेजेंड्स 2k

एंडियन मुद्दा मानक को अपरिभाषित व्यवहार के रूप में घोषित करने के लिए मजबूर नहीं करता है - reinterpret_cast में समान एंडियन मुद्दे हैं, लेकिन कार्यान्वयन परिभाषित व्यवहार है।
जोएग

1
@ legends2k, समस्या यह है कि ऑप्टिमाइज़र यह मान सकता है कि uint32_t को uint8_t पर लिखकर संशोधित नहीं किया गया है और इसलिए जब आप यह मान लेते हैं कि आपको गलत मूल्य मिल गया है, तो जब तक आप पहुँचते हैं, तब तक @Joe, अपरिभाषित व्यवहार प्रकट होता है। सूचक (मुझे पता है, कुछ अपवाद हैं)।
एपीग्रामग्राम

1
@ legends2k / AProgrammer: एक रीइंटरप्रिट_कास्ट के परिणाम को परिभाषित किया गया है। लौटाए गए सूचक का उपयोग करने से अपरिभाषित व्यवहार नहीं होता है, केवल कार्यान्वयन परिभाषित व्यवहार में। दूसरे शब्दों में, व्यवहार निरंतर और परिभाषित होना चाहिए, लेकिन यह पोर्टेबल नहीं है।
जोएग

1
@ legends2k: कोई भी सभ्य ऑप्टिमाइज़र बिटवे ऑपरेशंस को पहचान लेगा जो एक पूरी बाइट का चयन करता है और बाइट को पढ़ने / लिखने के लिए कोड जनरेट करता है, जो कि यूनियन के समान है लेकिन अच्छी तरह से परिभाषित (और पोर्टेबल) है। जैसे uint8_t getRed () const {रिटर्न कलर & 0x000000FF; } शून्य सेटेड (uint8_t r) {color = (रंग और ~ 0x000000FF) | आर; }
बेन वोइगट

22

मेरे पास नियमित रूप से आने वाला सबसे आम उपयोग अलियासिंग हैunion

निम्नलिखित को धयान मे रखते हुए:

union Vector3f
{
  struct{ float x,y,z ; } ;
  float elts[3];
}

यह क्या करता है? यह एक की स्वच्छ, साफ उपयोग की अनुमति देता Vector3f vec;द्वारा की सदस्य या तो नाम:

vec.x=vec.y=vec.z=1.f ;

या सरणी में पूर्णांक पहुंच द्वारा

for( int i = 0 ; i < 3 ; i++ )
  vec.elts[i]=1.f;

कुछ मामलों में, नाम से अभिगम करना सबसे स्पष्ट काम है जो आप कर सकते हैं। अन्य मामलों में, विशेष रूप से जब धुरी को प्रोग्रामेटिक रूप से चुना जाता है, तो ऐसा करने के लिए आसान बात यह है कि अक्ष को संख्यात्मक सूचकांक तक पहुँचा जाए - 0 के लिए x, y के लिए 1 और z के लिए 2।


3
इसे type-punningप्रश्न में उल्लिखित भी कहा जाता है। साथ ही प्रश्न में उदाहरण एक समान उदाहरण दिखाता है।
लीजेंड्स 2k

4
यह टाइपिंग नहीं है। मेरे उदाहरण में प्रकार मेल खाते हैं , इसलिए कोई "दंड" नहीं है, यह केवल उपनाम है।
बोब्बोबो

3
हां, लेकिन फिर भी, भाषा मानक के एक निरपेक्ष दृष्टिकोण से, से लिखा और पढ़ा जाने वाला सदस्य अलग-अलग हैं, जो कि प्रश्न में वर्णित के रूप में अपरिभाषित है।
लीजेंड्स 2k

3
मुझे उम्मीद है कि भविष्य के मानक इस विशेष मामले को "सामान्य प्रारंभिक बाद" नियम के तहत अनुमति दी जाएगी। हालाँकि, वर्तमान नियम के तहत सरणियाँ उस नियम में भाग नहीं लेती हैं।
बेन वोइगट

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

10

जैसा कि आप कहते हैं, यह कड़ाई से अपरिभाषित व्यवहार है, हालांकि यह कई प्लेटफार्मों पर "काम" करेगा। यूनियनों का उपयोग करने का असली कारण भिन्न रिकॉर्ड बनाना है।

union A {
   int i;
   double d;
};

A a[10];    // records in "a" can be either ints or doubles 
a[0].i = 42;
a[1].d = 1.23;

बेशक, आपको यह कहने के लिए किसी तरह के विभेदक की आवश्यकता है कि वास्तव में संस्करण क्या है। और ध्यान दें कि C ++ यूनियनों में बहुत अधिक उपयोग नहीं किया जाता है क्योंकि वे केवल POD प्रकारों को शामिल कर सकते हैं - प्रभावी रूप से बिना बिल्डरों और विनाशकों के।


क्या आपने इसका उपयोग इस प्रकार किया है (जैसे प्रश्न में) ?? :)
किंवदंतियों

यह थोड़ा कठिन है, लेकिन मैं "भिन्न रिकॉर्ड" को स्वीकार नहीं करता हूं। यही है, मुझे यकीन है कि वे मन में थे, लेकिन अगर वे एक प्राथमिकता थी तो उन्हें क्यों नहीं प्रदान किया गया? "बिल्डिंग ब्लॉक प्रदान करें क्योंकि यह अन्य चीजों के निर्माण के लिए उपयोगी हो सकता है" बस सहज रूप से अधिक संभावना है। विशेष रूप से कम से कम एक और आवेदन दिया गया था जो शायद ध्यान में था - मेमोरी मैप्ड I / O रजिस्टर, जहां इनपुट और आउटपुट रजिस्टर (जबकि ओवरलैप्ड) अपने नाम, प्रकार आदि के साथ अलग-अलग संस्थाएं हैं
स्टीव 314

@ Stev314 अगर उनके दिमाग में यह प्रयोग था, तो वे इसे अपरिभाषित व्यवहार नहीं कर सकते थे।

@ नई: अपरिभाषित व्यवहार किए बिना वास्तविक उपयोग के बारे में कहने के लिए पहली बार +1। मुझे लगता है कि वे इसे लागू कर सकते हैं इसे अन्य प्रकार के पिंगेटिंग ऑपरेशन (रीइंटरप्रिट_कास्ट, आदि) की तरह परिभाषित किया गया है। लेकिन जैसा मैंने पूछा, क्या आपने इसे टाइप-पिंगेट के लिए इस्तेमाल किया है?
लीजेंड्स 2

@ नील - मेमोरी-मैप्ड रजिस्टर उदाहरण अपरिभाषित नहीं है, सामान्य एंडियन / आदि एक तरफ और "अस्थिर" ध्वज दिया गया है। इस मॉडल में एक पते पर लिखना एक ही पते को पढ़ने के समान रजिस्टर का संदर्भ नहीं देता है। इसलिए कोई "क्या आप वापस पढ़ रहे हैं" समस्या है क्योंकि आप वापस नहीं पढ़ रहे हैं - आपने उस पते पर जो भी आउटपुट लिखा है, जब आप पढ़ते हैं तो आप एक स्वतंत्र इनपुट पढ़ रहे हैं। एकमात्र मुद्दा यह सुनिश्चित कर रहा है कि आप संघ के इनपुट पक्ष को पढ़ते हैं और आउटपुट पक्ष लिखते हैं। एम्बेडेड सामान में आम था - शायद अभी भी है।
स्टीव314 12

8

सी में यह एक वैरिएंट की तरह कुछ को लागू करने का एक अच्छा तरीका था।

enum possibleTypes{
  eInt,
  eDouble,
  eChar
}


struct Value{

    union Value {
      int iVal_;
      double dval;
      char cVal;
    } value_;
    possibleTypes discriminator_;
} 

switch(val.discriminator_)
{
  case eInt: val.value_.iVal_; break;

लिटल मेमोरी के समय में यह संरचना किसी सदस्य की तुलना में कम मेमोरी का उपयोग कर रही है।

वैसे C प्रदान करता है

    typedef struct {
      unsigned int mantissa_low:32;      //mantissa
      unsigned int mantissa_high:20;
      unsigned int exponent:11;         //exponent
      unsigned int sign:1;
    } realVal;

बिट मानों तक पहुँचने के लिए।


यद्यपि आपके दोनों उदाहरण मानक में पूरी तरह से परिभाषित हैं; लेकिन, हे, बिट फ़ील्ड्स का उपयोग करना निश्चित है कि यह अविश्वसनीय कोड शॉट है, है ना?
लीजेंड्स 2

नहीं, यह नहीं है। जहाँ तक मुझे पता है कि इसका व्यापक रूप से समर्थन किया गया है।
टोटोंगा

1
कंपाइलर समर्थन पोर्टेबल में अनुवाद नहीं करता है। सी बुक : सी (इस प्रकार सी ++) मशीन शब्दों के भीतर फ़ील्ड के आदेश की कोई गारंटी नहीं देता है, इसलिए यदि आप उन्हें बाद के कारण के लिए उपयोग करते हैं, तो आप प्रोग्राम न केवल गैर-पोर्टेबल होंगे, यह कंपाइलर-निर्भर भी होगा।
किंवदंतियों 2

5

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


2
क्या कोई एंडियन समस्या नहीं है? "अपरिभाषित" की तुलना में एक अपेक्षाकृत आसान फिक्स, लेकिन कुछ परियोजनाओं के लिए खाते में लेने के लायक यदि ऐसा है।
स्टीव 314

5

C ++ में, Boost Variant संघ के एक सुरक्षित संस्करण को लागू करता है, जिसे अपरिभाषित व्यवहार को यथासंभव रोकने के लिए डिज़ाइन किया गया है।

इसका प्रदर्शन enum + unionनिर्माण के समान है (ढेर भी आवंटित किया गया है) लेकिन यह इसके बजाय प्रकारों की एक टेम्पलेट सूची का उपयोग करता है enum:)


5

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

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


4

तकनीकी रूप से यह अपरिभाषित है, लेकिन वास्तव में अधिकांश (सभी?) संकलक इसे reinterpret_castएक प्रकार से दूसरे प्रकार का उपयोग करने के समान मानते हैं , जिसके परिणामस्वरूप कार्यान्वयन को परिभाषित किया जाता है। मैं आपके वर्तमान कोड पर नींद नहीं खोऊंगा।


" एक प्रकार से दूसरे प्रकार की एक पुन: व्याख्या_कास्ट, जिसका परिणाम कार्यान्वयन को परिभाषित किया गया है। " नहीं, यह नहीं है। कार्यान्वयन को इसे परिभाषित नहीं करना है, और अधिकांश इसे परिभाषित नहीं करते हैं। इसके अलावा, एक सूचक को कुछ यादृच्छिक मान कास्टिंग करने के लिए अनुमत कार्यान्वयन परिभाषित व्यवहार क्या होगा?
जिज्ञासु

4

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


4

दूसरों ने वास्तुकला के अंतरों का उल्लेख किया है (छोटे - बड़े एंडियन)।

मैंने इस समस्या को पढ़ा कि चूंकि चर के लिए मेमोरी साझा की जाती है, तो एक को लिखकर, दूसरे बदलते हैं और, उनके प्रकार के आधार पर, मूल्य निरर्थक हो सकता है।

जैसे। संघ {फ्लोट एफ; int i; } एक्स;

Xi को लिखना तब व्यर्थ होगा जब आप xf से पढ़ते हैं - जब तक कि आप फ्लोट के साइन, एक्सपोनेंट या मंटिसा घटकों को देखने के लिए ऐसा न करें।

मुझे लगता है कि संरेखण का एक मुद्दा भी है: यदि कुछ चर शब्द संरेखित होने चाहिए तो आपको अपेक्षित परिणाम नहीं मिल सकता है।

जैसे। संघ {चार सी [4]; int i; } एक्स;

यदि, काल्पनिक रूप से, किसी मशीन पर एक वर्ण को शब्द संरेखित किया जाना था तो c [0] और c [1] के साथ भंडारण साझा किया जाएगा लेकिन c [2] और c [3] नहीं।


एक बाइट जिसे शब्द संरेखित किया जाना है? इसका कोई अर्थ नही बन रहा है। एक बाइट की परिभाषा से कोई संरेखण की आवश्यकता नहीं है।
जिज्ञासु

हां, मुझे शायद बेहतर उदाहरण का इस्तेमाल करना चाहिए था। धन्यवाद।
फिल्कोब्बरॉन

@ गंभीर: ऐसे कई मामले हैं, जिनमें बाइट्स के सरणियों को शब्द-संरेखित करने की इच्छा हो सकती है। यदि किसी के पास 1024 बाइट्स के कई सरणियाँ हैं और अक्सर एक दूसरे को कॉपी करने की इच्छा होगी, तो उन्हें शब्द गठबंधन करने से कई प्रणालियों पर एक memcpy()से दूसरे की गति दोगुनी हो सकती है । कुछ सिस्टम सट्टेबाज़ी से char[]आवंटन को संरेखित कर सकते हैं जो संरचनाओं और यूनियनों के बाहर होते हैं और अन्य कारणों से। मौजूदा उदाहरण में, यह धारणा कि गैर-पोर्टेबल iतत्वों के लिए सभी को ओवरलैप किया जाएगा c[], लेकिन ऐसा इसलिए है क्योंकि इसकी कोई गारंटी नहीं है sizeof(int)==4
सुपरकैट

4

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

जब संरचना सदस्यों को अपने स्वयं के नामस्थान सौंपे गए थे, तो एक ही विस्थापन के साथ दो संरचना सदस्यों की घोषणा करना असंभव हो गया। भाषा में यूनियनों को जोड़ने से उन्हीं शब्दार्थों को प्राप्त करना संभव हो गया जो भाषा के पुराने संस्करणों में उपलब्ध थे (हालांकि एक संलग्न प्रसंग को निर्यात किए गए नामों की अक्षमता अभी भी फ़ू को बदलने के लिए एक खोज / जगह का उपयोग करके आवश्यक हो सकती है -> सदस्य foo-> type1.member में)। जो महत्वपूर्ण था वह इतना अधिक नहीं था कि जिन लोगों ने यूनियनों को जोड़ा है, उनके मन में कोई विशेष लक्ष्य का उपयोग होता है, बल्कि यह कि वे एक ऐसा साधन प्रदान करते हैं, जिससे प्रोग्रामर जो पहले के शब्दार्थों पर निर्भर थे, जो भी उद्देश्य के लिए , अभी भी प्राप्त करने में सक्षम होना चाहिए। समान शब्दार्थ भले ही उन्हें इसे करने के लिए एक अलग वाक्यविन्यास का उपयोग करना पड़े।


इतिहास सबक की सराहना, हालांकि मानक इस तरह के और इस तरह के परिभाषित करने, के रूप में अपरिभाषित जो बीते सी युग में जहाँ कश्मीर एंड आर पुस्तक थी केवल "मानक" में ऐसा नहीं था के साथ, एक का उपयोग न करने में यकीन हो गया है जो कुछ भी उद्देश्य के लिए और UB भूमि दर्ज करें।
लीजेंड्स 2 के सी

2
@ किंवदंतियों 2k: जब मानक लिखा गया था, तो सी कार्यान्वयन के बहुमत ने यूनियनों को उसी तरह से व्यवहार किया, और इस तरह के उपचार उपयोगी थे। कुछ, हालांकि, और मानक के लेखकों ने किसी भी मौजूदा कार्यान्वयन को "गैर-अनुरूपता" के रूप में ब्रांड करने के लिए घृणा की थी। इसके बजाय, उन्हें लगा कि अगर कार्यान्वयनकर्ताओं को उन्हें कुछ करने के लिए कहने के लिए मानक की आवश्यकता नहीं है (जैसा कि इस तथ्य से स्पष्ट है कि वे पहले से ही ऐसा कर रहे थे ), तो इसे अनिर्दिष्ट या अपरिभाषित छोड़ देना बस यथास्थिति को बनाए रखेगा । यह धारणा कि चीजों को कम परिभाषित किया जाना चाहिए, जैसा कि मानक के लिखे जाने से पहले था ...
सुपरकैट

2
... एक बहुत अधिक हालिया नवाचार लगता है। इस सब के बारे में विशेष रूप से दुख की बात है कि यदि उच्च अंत अनुप्रयोगों को लक्षित करने वाले कंपाइलर लेखकों को यह पता लगाना था कि 1990 के दशक में लागू किए गए अधिकांश कंपाइलरों के लिए उपयोगी अनुकूलन निर्देशों को कैसे जोड़ा जाए, न कि सुविधाओं और गारंटी के बजाय जो केवल "द्वारा समर्थित किया गया था। "90% कार्यान्वयन, परिणाम एक ऐसी भाषा होगी जो हाइपर-आधुनिक सी की तुलना में बेहतर और मज़बूती से प्रदर्शन कर सकती है
सुपरकाट

2

आप दो मुख्य कारणों के लिए आ संघ का उपयोग कर सकते हैं :

  1. विभिन्न तरीकों से एक ही डेटा तक पहुंचने का एक आसान तरीका, जैसे आपके उदाहरण में
  2. अंतरिक्ष को बचाने का एक तरीका जब विभिन्न डेटा सदस्य होते हैं, जिसमें से केवल एक ही कभी 'सक्रिय' हो सकता है

1 क्या वास्तव में सी-स्टाइल हैक से शॉर्ट-कट राइटिंग कोड के आधार पर आप जानते हैं कि लक्ष्य प्रणाली की मेमोरी आर्किटेक्चर कैसे काम करती है। जैसा कि पहले ही कहा गया है कि यदि आप वास्तव में बहुत सारे प्लेटफार्मों को लक्षित नहीं करते हैं, तो आप सामान्य रूप से इससे दूर हो सकते हैं। मेरा मानना ​​है कि कुछ संकलक आपको पैकिंग निर्देश का उपयोग करने की अनुमति दे सकते हैं (मुझे पता है कि वे संरचनाओं पर करते हैं)?

2. का एक अच्छा उदाहरण COM में बड़े पैमाने पर उपयोग किए जाने वाले VARIANT प्रकार में पाया जा सकता है ।


2

जैसा कि दूसरों ने उल्लेख किया है, यूनियनों के साथ संयुक्त और संरचनाओं में लिपटे यूनियनों का उपयोग टैग किए गए यूनियनों को लागू करने के लिए किया जा सकता है। रुस्तस को लागू करने के लिए एक व्यावहारिक उपयोग है Result<T, E>, जो मूल रूप से शुद्ध का उपयोग करके कार्यान्वित किया जाता है enum(जंग वास में अतिरिक्त डेटा पकड़ सकता है)। यहाँ एक C ++ उदाहरण है:

template <typename T, typename E> struct Result {
    public:
    enum class Success : uint8_t { Ok, Err };
    Result(T val) {
        m_success = Success::Ok;
        m_value.ok = val;
    }
    Result(E val) {
        m_success = Success::Err;
        m_value.err = val;
    }
    inline bool operator==(const Result& other) {
        return other.m_success == this->m_success;
    }
    inline bool operator!=(const Result& other) {
        return other.m_success != this->m_success;
    }
    inline T expect(const char* errorMsg) {
        if (m_success == Success::Err) throw errorMsg;
        else return m_value.ok;
    }
    inline bool is_ok() {
        return m_success == Success::Ok;
    }
    inline bool is_err() {
        return m_success == Success::Err;
    }
    inline const T* ok() {
        if (is_ok()) return m_value.ok;
        else return nullptr;
    }
    inline const T* err() {
        if (is_err()) return m_value.err;
        else return nullptr;
    }

    // Other methods from https://doc.rust-lang.org/std/result/enum.Result.html

    private:
    Success m_success;
    union _val_t { T ok; E err; } m_value;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.