निष्क्रिय संघ सदस्य और अपरिभाषित व्यवहार तक पहुँच?


129

मैं इस धारणा के तहत था कि unionपिछले एक सेट के अलावा किसी सदस्य को एक्सेस करना यूबी है, लेकिन मैं एक ठोस संदर्भ (यह यूबी का दावा करने वाले उत्तर के अलावा अन्य नहीं है, लेकिन मानक के किसी भी समर्थन के बिना) प्रतीत नहीं कर सकता।

तो, क्या यह अपरिभाषित व्यवहार है?


3
C99 (और मेरा मानना ​​है कि C ++ 11 के साथ-साथ) स्पष्ट रूप से यूनियनों के साथ टाइप-पाइंटिंग की अनुमति देते हैं। इसलिए मुझे लगता है कि यह "कार्यान्वयन परिभाषित" व्यवहार के तहत आता है।
रहस्यमयी जूल

1
मैंने इसे कई अवसरों पर व्यक्तिगत इंट से चार में बदलने के लिए उपयोग किया है। इसलिए, मैं निश्चित रूप से जानता हूं कि यह अपरिभाषित नहीं है। मैंने इसे सन सीसी कंपाइलर पर इस्तेमाल किया। तो, यह अभी भी संकलक पर निर्भर हो सकता है।
गो ० ४

42
@ go4sri: जाहिर है, आप नहीं जानते कि व्यवहार के अपरिभाषित होने का क्या मतलब है। यह तथ्य कि यह किसी समय में आपके लिए काम करता है, इसकी अपरिभाषितता के विपरीत नहीं है।
बेंजामिन लिंडले


4
@ मीडिया, आप जिस ब्लॉग पोस्ट से लिंक करते हैं वह विशेष रूप से C99 के बारे में है; यह प्रश्न केवल C ++ के लिए टैग किया गया है।
davmac

जवाबों:


131

भ्रम यह है कि सी स्पष्ट रूप से एक संघ के माध्यम से टाइप-पाइंटिंग की अनुमति देता है, जबकि सी ++ () की ऐसी कोई अनुमति नहीं है।

6.5.2.3 संरचना और संघ के सदस्य

95) यदि सदस्य किसी संघ वस्तु की सामग्री को पढ़ने के लिए उपयोग नहीं करता है, तो अंतिम रूप से सदस्य का उपयोग वस्तु में मूल्य को संग्रहीत करने के लिए किया जाता है, मूल्य के वस्तु प्रतिनिधित्व का उचित हिस्सा नए में ऑब्जेक्ट प्रतिनिधित्व के रूप में पुनर्व्याख्या किया जाता है जैसा कि 6.2.6 में वर्णित है (एक प्रक्रिया जिसे कभी-कभी 'टाइप पिंगिंग' कहा जाता है)। यह एक जाल प्रतिनिधित्व हो सकता है।

C ++ के साथ स्थिति:

9.5 यूनियनों [class.union]

एक संघ में, गैर-स्थैतिक डेटा सदस्यों में से अधिकांश किसी भी समय सक्रिय हो सकता है, अर्थात, गैर-स्थैतिक डेटा सदस्यों में से अधिकांश का मूल्य किसी भी समय संघ में संग्रहीत किया जा सकता है।

C ++ में बाद में structसामान्य प्रारंभिक अनुक्रमों के साथ s यूनियनों के उपयोग की अनुमति देने वाली भाषा है ; हालांकि यह टाइप-पैंटिंग की अनुमति नहीं देता है।

यह निर्धारित करने के लिए कि क्या C ++ में यूनियन टाइप-पैंटिंग की अनुमति है, हमें और खोज करनी होगी। याद करें कि C ++ 11 के लिए एक मानक संदर्भ है (और C99 में C11 की समान भाषा है जो यूनियन टाइप-पिंगिंग है):

3.9 प्रकार [basic.types]

4 - टाइप टी के ऑब्जेक्ट का ऑब्जेक्ट प्रतिनिधित्व N का अनुक्रम है जो टाइप T के ऑब्जेक्ट द्वारा उठाए गए अहस्ताक्षरित चार ऑब्जेक्ट हैं, जहां एन आकार आकार (टी) के बराबर होता है। किसी वस्तु का मूल्य निरूपण बिट्स का सेट है जो टाइप T का मान रखता है। तुच्छ रूप से प्रतिलिपि योग्य प्रकारों के लिए, मूल्य प्रतिनिधित्व ऑब्जेक्ट प्रतिनिधित्व में बिट्स का एक सेट है जो एक मूल्य निर्धारित करता है, जो कार्यान्वयन का एक असतत तत्व है- मानों का सेट 42
42) आशय यह है कि C ++ का मेमोरी मॉडल ISO / IEC 9899 प्रोग्रामिंग लैंग्वेज C के अनुकूल है।

जब हम पढ़ते हैं तो यह विशेष रूप से दिलचस्प हो जाता है

3.8 वस्तु जीवनकाल [बुनियादी। जीवन]

एक प्रकार की वस्तु T का जीवनकाल तब शुरू होता है जब: - प्रकार T के लिए उचित संरेखण और आकार के साथ भंडारण प्राप्त किया जाता है, और - यदि वस्तु में गैर-तुच्छ आरंभीकरण है, तो इसका आरंभीकरण पूरा हो गया है।

तो एक आदिम प्रकार के लिए (जिसमें ipso facto में तुच्छ इनिशियलाइज़ेशन है) एक संघ में निहित है, वस्तु का जीवनकाल कम से कम यूनियन के जीवनकाल को शामिल करता है। यह हमें आह्वान करने की अनुमति देता है

3.9.2 यौगिक प्रकार [basic.compound]

यदि टाइप A का ऑब्जेक्ट पता A पर स्थित है, तो टाइप Cv T * का पॉइंटर जिसका मान A पता है, उस ऑब्जेक्ट को इंगित करने के लिए कहा जाता है, भले ही मान कैसे प्राप्त किया गया हो।

यह मानते हुए कि जिस ऑपरेशन में हम रुचि रखते हैं, वह टाइप-पेन्जिमेंट है अर्थात एक गैर-सक्रिय संघ के सदस्य का मान लेना, और ऊपर दिया गया है कि हमारे पास उस सदस्य द्वारा संदर्भित ऑब्जेक्ट का एक वैध संदर्भ है, वह ऑपरेशन है -वृद्धि रूपांतरण:

४.१ लव-टू-रैवल्यू रूपांतरण [conv.lval]

एक गैर-फ़ंक्शन, गैर-सरणी प्रकार की Tएक चमक को एक प्रचलन में परिवर्तित किया जा सकता है। यदि Tएक अधूरा प्रकार है, तो इस रूपांतरण की आवश्यकता वाला एक प्रोग्राम बीमार है। यदि जिस वस्तु से ग्लव्यू संदर्भित होता है वह ऑब्जेक्ट प्रकार की वस्तु Tनहीं है और यह व्युत्पन्न प्रकार का ऑब्जेक्ट नहीं है T, या यदि ऑब्जेक्ट अनइंस्टालिज्ड है , तो इस रूपांतरण के लिए आवश्यक प्रोग्राम को unde। Ned व्यवहार है।

फिर सवाल यह है कि क्या एक गैर-सक्रिय संघ के सदस्य एक वस्तु को सक्रिय संघ के सदस्य को भंडारण द्वारा प्रारंभ किया जाता है। जहाँ तक मैं बता सकता हूँ, यह मामला नहीं है और इसलिए यदि:

  • एक यूनियन को charऐरे स्टोरेज और बैक (3.9: 2), या में कॉपी किया जाता है
  • एक संघ को उसी प्रकार के दूसरे संघ में कॉपी किया जाता है (3.9: 3), या
  • ISO / IEC 9899 (अब तक परिभाषित किया गया है) के अनुरूप एक संघ तत्व द्वारा भाषा सीमाओं के पार एक संघ तक पहुँचा जा सकता है (3.9: 4 नोट 42), फिर

एक गैर-सक्रिय सदस्य द्वारा एक संघ तक पहुंच को परिभाषित किया गया है और वस्तु और मूल्य प्रतिनिधित्व का पालन करने के लिए परिभाषित किया गया है, ऊपर दिए गए किसी भी एक के बिना उपयोग अपरिभाषित व्यवहार है। इस तरह के एक कार्यक्रम पर प्रदर्शन करने की अनुमति के लिए इसके निहितार्थ हैं, क्योंकि कार्यान्वयन बेशक यह मान सकता है कि अपरिभाषित व्यवहार नहीं होता है।

यह है, हालांकि हम वैध रूप से एक गैर-सक्रिय संघ के सदस्य के लिए एक अंतराल बना सकते हैं (यही वजह है कि निर्माण के बिना एक गैर-सक्रिय सदस्य को असाइन करना ठीक है) इसे अनैतिक रूप से माना जाता है।


5
3.8 / 1 कहता है कि किसी वस्तु का जीवनकाल तब समाप्त होता है जब उसका भंडारण पुनः उपयोग किया जाता है। यह मुझे इंगित करता है कि एक संघ के जीवनकाल के एक गैर-सक्रिय सदस्य समाप्त हो गया है क्योंकि इसका भंडारण सक्रिय सदस्य के लिए पुन: उपयोग किया गया है। इसका मतलब है कि आप सदस्य (3.8 / 6) का उपयोग करने में सीमित हैं।
bames53

2
उस व्याख्या के तहत फिर प्रत्येक मेमोरी में एक साथ सभी प्रकार की वस्तुएं होती हैं जो तुच्छ रूप से आरंभ करने योग्य होती हैं और उनमें उपयुक्त संरेखण होता है ... तो क्या किसी भी गैर-तुच्छ रूप से आरंभ करने वाले प्रकार का जीवनकाल तुरंत समाप्त हो जाता है क्योंकि इसका भंडारण अन्य सभी प्रकारों के लिए पुन: उपयोग किया जाता है ( और पुनः आरंभ न करें क्योंकि वे तुच्छ रूप से आरंभ करने योग्य नहीं हैं)?
bames53

3
शब्द 4.1 पूरी तरह से और पूरी तरह से टूट गया है और तब से फिर से लिखा गया है। यह पूरी तरह से वैध चीजों के सभी प्रकार की अनुमति नहीं है: यह कस्टम अनुमति नहीं दी memcpyकार्यान्वयन (का उपयोग कर वस्तुओं तक पहुँचने unsigned charlvalues), यह करने के लिए पहुंच की अनुमति नहीं *pके बाद int *p = 0; const int *const *pp = &p;(भले ही से अंतर्निहित रूपांतरण int**के लिए const int*const*मान्य है), तो यह और भी एक्सेस करने की अनुमति नहीं cके बाद struct S s; const S &c = s;CWG अंक 616 । क्या नया शब्द इसे अनुमति देता है? वहाँ भी है [basic.lval]।

2
@Omnifarious: यह समझ में आता है, हालांकि इसे भी स्पष्ट करना होगा (और C मानक को भी स्पष्ट करने की आवश्यकता है, btw) &जब एक यूनियन सदस्य पर लागू होता है, तो अपर ऑपरेटर का क्या मतलब है। मुझे लगता है कि परिणामी सूचक को कम से कम अगली बार सदस्य तक पहुंचने के लिए उपयोग करने योग्य होना चाहिए, जब तक कि अगली बार किसी अन्य सदस्य के प्रत्यक्ष या अप्रत्यक्ष उपयोग के लिए, लेकिन जीसीसी में सूचक लंबे समय तक भी उपयोग करने योग्य नहीं होता है, जो एक सवाल उठाता है &ऑपरेटर मतलब माना जाता है।
सुपरकैट

4
"रिकॉल के बारे में एक प्रश्न कि C99 C ++ 11 के लिए एक मानक संदर्भ है" केवल प्रासंगिक नहीं है, जहां c ++ मानक स्पष्ट रूप से C मानक (उदाहरण के लिए c लाइब्रेरी फ़ंक्शंस) को संदर्भित करता है?
माइक एमबी

28

C ++ 11 मानक इसे इस तरह कहते हैं

9.5 यूनियनों

एक संघ में, गैर-स्थैतिक डेटा सदस्यों में से अधिकांश किसी भी समय सक्रिय हो सकता है, अर्थात, गैर-स्थैतिक डेटा सदस्यों में से अधिकांश का मूल्य किसी भी समय संघ में संग्रहीत किया जा सकता है।

यदि केवल एक मूल्य संग्रहीत है, तो आप दूसरे को कैसे पढ़ सकते हैं? यह सिर्फ वहाँ नहीं है।


Gcc डॉक्यूमेंटेशन इसे इंप्लीमेंट डिफाइन्ड बिहेवियर के तहत लिस्ट करता है

  • एक यूनियन ऑब्जेक्ट के एक सदस्य को एक अलग प्रकार (C90 6.3.2.3) के सदस्य का उपयोग करके एक्सेस किया जाता है।

ऑब्जेक्ट के प्रतिनिधित्व के प्रासंगिक बाइट्स को एक्सेस के लिए उपयोग किए जाने वाले प्रकार के ऑब्जेक्ट के रूप में माना जाता है। टाइप-पाइंट देखें। यह एक ट्रैप प्रतिनिधित्व हो सकता है।

यह दर्शाता है कि सी मानक द्वारा इसकी आवश्यकता नहीं है।


2016-01-05: टिप्पणियों के माध्यम से मैं C99 दोष रिपोर्ट # 283 से जुड़ा हुआ था, जो C मानक दस्तावेज़ में फुटनोट के समान पाठ जोड़ता है:

78a) यदि सदस्य किसी संघ वस्तु की सामग्री का उपयोग करने के लिए उपयोग किया जाता है, जैसा कि सदस्य पिछली बार वस्तु में एक मूल्य को संग्रहीत करने के लिए उपयोग नहीं करता है, तो मूल्य के वस्तु प्रतिनिधित्व का उचित हिस्सा नए में एक वस्तु प्रतिनिधित्व के रूप में पुन: व्याख्या किया जाता है। जैसा कि 6.2.6 में वर्णित है (एक प्रक्रिया जिसे कभी-कभी "टाइप पिंगिंग" कहा जाता है)। यह एक जाल प्रतिनिधित्व हो सकता है।

सुनिश्चित नहीं है कि यह बहुत स्पष्ट करता है, यह देखते हुए कि एक फुटनोट मानक के लिए आदर्श नहीं है।


10
@LuchianGrigore: UB वह नहीं है जो मानक कहता है UB, इसके बजाय यह है कि मानक क्या वर्णन करता है कि उसे कैसे काम करना चाहिए। यह ठीक ऐसा ही मामला है। क्या मानक वर्णन करता है कि क्या होता है? क्या यह कहता है कि यह कार्यान्वयन परिभाषित है? नहीं और नहीं। तो यह यूबी है। इसके अलावा, "सदस्य समान मेमोरी एड्रेस साझा करते हैं" तर्क के बारे में, आपको अलियासिंग नियमों का उल्लेख करना होगा, जो आपको यूबी में फिर से लाएगा।
याकोव गल्का

5
@ लुचियन: यह काफी स्पष्ट है कि सक्रिय का क्या मतलब है, "
बेंजामिन लिंडले

5
@LuchianGrigore: हाँ वहाँ हैं। ऐसे मामलों की अनंत राशि है जो मानक (और नहीं कर सकते) का पता नहीं है। (C ++ एक ट्यूरिंग पूर्ण VM है इसलिए यह अपूर्ण है।) तो क्या? यह समझाता है कि "सक्रिय" का क्या मतलब है, "वह है" के बाद उपरोक्त उद्धरण देखें।
याकोव गल्का

8
@LuchianGrigore: व्यवहार की स्पष्ट परिभाषा का प्रवेश भी निश्चित खंड के अनुसार अपरिभाषित व्यवहार नहीं है।
jxh

5
@ कैलाडु एक अलग कारण से यूबी है - यह सख्त अलियासिंग का उल्लंघन करता है।
रहस्यपूर्ण

18

मुझे लगता है कि निकटतम मानक यह कहने के लिए आता है कि यह अपरिभाषित व्यवहार है जहां यह एक सामान्य प्रारंभिक अनुक्रम वाले संघ के लिए व्यवहार को परिभाषित करता है (C99, §6.5.2.3 / 5):

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

C ++ 11 ++9.2 / 19 में समान आवश्यकताएं / अनुमति देता है:

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

हालाँकि यह न तो सीधे तौर पर बताता है, ये दोनों एक मजबूत निहितार्थ रखते हैं कि "निरीक्षण करना" (पढ़ना) एक सदस्य को "अनुमति दी जाती है" केवल अगर 1) तो यह सबसे हाल ही में लिखे गए सदस्य का हिस्सा है, या 2) एक आम प्रारंभिक का हिस्सा है अनुक्रम।

यह एक प्रत्यक्ष कथन नहीं है जो अन्यथा करना अपरिभाषित व्यवहार है, लेकिन यह सबसे निकटतम है जिसके बारे में मैं जानता हूं।


इसे पूरा करने के लिए, आपको यह जानना होगा कि C ++ के लिए "लेआउट-संगत प्रकार" क्या हैं, या "संगत प्रकार" C. के लिए हैं
माइकल एंडरसन

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

यह "सामान्य प्रारंभिक अनुक्रम" बात शायद मेरे प्रोजेक्ट्स के 2 या 3 को रीराइट बिन से बचा सकती है। जब मैं पहली बार unionअपरिभाषित हो रहा था, तब से मैं इसके बारे में पढ़ रहा था , क्योंकि मैं एक विशेष ब्लॉग द्वारा आभास दिया गया था कि यह ठीक था, और इसके आसपास कई बड़ी संरचनाओं और परियोजनाओं का निर्माण किया। अब मुझे लगता है कि मैं सब के बाद ठीक हो सकता हूं, क्योंकि मेरे unionवर्गों में सामने वाले वर्ग समान होते हैं
अंडरस्कोर_ड

@JerryCoffin, मुझे लगता है कि तुम मुझे के रूप में ही सवाल पर इशारा गया: क्या हमारे अगर unionहोता है जैसे एक uint8_tऔर एक class Something { uint8_t myByte; [...] };- मैं इस प्रावधान भी यहाँ लागू होगा ग्रहण करेंगे, लेकिन यह बहुत जानबूझ कर केवल के लिए अनुमति देने के लिए शब्दों है structरों। सौभाग्य से मैं पहले से ही कच्चे आदिम के बजाय उन का उपयोग कर रहा हूं: ओ
अंडरस्कोर_ड

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

12

उपलब्ध उत्तरों द्वारा अभी तक उल्लेखित कुछ नहीं है, खंड 6.2.5 के अनुच्छेद 21 में फुटनोट 37 है:

ध्यान दें कि कुल प्रकार में संघ प्रकार शामिल नहीं है क्योंकि संघ प्रकार के साथ एक वस्तु में एक समय में केवल एक सदस्य हो सकता है।

यह आवश्यकता स्पष्ट रूप से प्रतीत होती है कि आपको एक सदस्य में नहीं लिखना चाहिए और किसी दूसरे में पढ़ना चाहिए। इस मामले में यह विनिर्देशन की कमी से अपरिभाषित व्यवहार हो सकता है।


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

-3

मैं इसे एक उदाहरण से समझाता हूं।
मान लें कि हमारे पास निम्नलिखित संघ हैं:

union A{
   int x;
   short y[2];
};

मैं अच्छी तरह से मान लेता हूं कि sizeof(int)4 देता है, और यह sizeof(short)2 देता है।
जब आप लिखते हैं union A a = {10}कि अच्छी तरह से टाइप ए का एक नया संस्करण बनाएं जिसमें मूल्य 10 हो।

आपकी स्मृति इस तरह दिखनी चाहिए: (याद रखें कि सभी यूनियन सदस्यों को एक ही स्थान मिलता है)

       | x |
       | y [0] | y [१] |
       -----------------------------------------
   a-> | 0000 0000 | 0000 0000 | 0000 0000 | 0000 1010 |
       -----------------------------------------

जैसा कि आप देख सकते हैं, कुल्हाड़ी का मूल्य 10 है, ay 1 का मान 10 है, और ay [0] का मान 0 है।

अब, अगर मैं ऐसा करूँ तो क्या होगा?

a.y[0] = 37;

हमारी स्मृति इस तरह दिखाई देगी:

       | x |
       | y [0] | y [१] |
       -----------------------------------------
   a-> | 0000 0000 | 0010 0101 | 0000 0000 | 0000 1010 |
       -----------------------------------------

यह कुल्हाड़ी के मूल्य को 2424842 (दशमलव में) कर देगा।

अब, यदि आपके संघ में फ्लोट, या डबल है, तो आपके मेमोरी मैप में गड़बड़ी अधिक है, जिस तरह से आप सटीक संख्याओं को स्टोर करते हैं। अधिक जानकारी आप यहाँ प्राप्त कर सकते हैं


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