जवाबों:
यूनियनों का उपयोग अक्सर पूर्णांक और फ़्लोट के द्विआधारी निरूपण के बीच परिवर्तित करने के लिए किया जाता है:
union
{
int i;
float f;
} u;
// Convert floating-point bits to integer:
u.f = 3.14159f;
printf("As integer: %08x\n", u.i);
यद्यपि यह सी मानक के अनुसार तकनीकी रूप से अपरिभाषित व्यवहार है (आप केवल उसी क्षेत्र को पढ़ना चाहते हैं जो हाल ही में लिखा गया था), यह वस्तुतः किसी भी संकलक में अच्छी तरह से परिभाषित तरीके से कार्य करेगा।
यूनियनों को कभी-कभी C में छद्म-बहुरूपता को लागू करने के लिए भी उपयोग किया जाता है, एक संरचना देकर कुछ टैग इंगित करता है कि इसमें किस प्रकार की वस्तु है, और फिर संभव प्रकारों को एक साथ मिलाकर:
enum Type { INTS, FLOATS, DOUBLE };
struct S
{
Type s_type;
union
{
int s_ints[2];
float s_floats[2];
double s_double;
};
};
void do_something(struct S *s)
{
switch(s->s_type)
{
case INTS: // do something with s->s_ints
break;
case FLOATS: // do something with s->s_floats
break;
case DOUBLE: // do something with s->s_double
break;
}
}
यह struct S
28 के बजाय केवल 12 बाइट्स का आकार देता है ।
एंबेडेड एंबेडेड प्रोग्रामिंग या उन स्थितियों में विशेष रूप से उपयोगी होते हैं जहां हार्डवेयर / मेमोरी की सीधी पहुंच की आवश्यकता होती है। यहाँ एक तुच्छ उदाहरण है:
typedef union
{
struct {
unsigned char byte1;
unsigned char byte2;
unsigned char byte3;
unsigned char byte4;
} bytes;
unsigned int dword;
} HW_Register;
HW_Register reg;
तो आप इस प्रकार के रूप में reg का उपयोग कर सकते हैं:
reg.dword = 0x12345678;
reg.bytes.byte3 = 4;
एंडियननेस (बाइट ऑर्डर) और प्रोसेसर आर्किटेक्चर निश्चित रूप से महत्वपूर्ण हैं।
एक अन्य उपयोगी विशेषता बिट संशोधक है:
typedef union
{
struct {
unsigned char b1:1;
unsigned char b2:1;
unsigned char b3:1;
unsigned char b4:1;
unsigned char reserved:4;
} bits;
unsigned char byte;
} HW_RegisterB;
HW_RegisterB reg;
इस कोड के साथ आप रजिस्टर / मेमोरी एड्रेस में सीधे सिंगल बिट एक्सेस कर सकते हैं:
x = reg.bits.b2;
निम्न स्तर की सिस्टम प्रोग्रामिंग एक उचित उदाहरण है।
IIRC, मैंने घटक बिट्स में हार्डवेयर रजिस्टरों को तोड़ने के लिए यूनियनों का उपयोग किया है। तो, आप एक 8-बिट रजिस्टर तक पहुँच सकते हैं (जैसा कि मैंने किया था, दिन में मैंने ऐसा किया ;-) घटक बिट्स में।
(मैं सटीक सिंटैक्स भूल जाता हूं लेकिन ...) यह संरचना एक नियंत्रण रजिस्टर को control_byte के रूप में या व्यक्तिगत बिट्स के माध्यम से एक्सेस करने की अनुमति देगा। किसी दिए गए एंडियन के लिए बिट्स के नक्शे को सही रजिस्टर बिट्स पर सुनिश्चित करना महत्वपूर्ण होगा।
typedef union {
unsigned char control_byte;
struct {
unsigned int nibble : 4;
unsigned int nmi : 1;
unsigned int enabled : 1;
unsigned int fired : 1;
unsigned int control : 1;
};
} ControlRegister;
मैंने इसे कुछ पुस्तकालयों में ऑब्जेक्ट ओरिएंटेड इनहेरिटेंस के प्रतिस्थापन के रूप में देखा है।
उदाहरण के लिए
Connection
/ | \
Network USB VirtualConnection
यदि आप चाहते हैं कि कनेक्शन "वर्ग" उपरोक्त दोनों में से एक हो, तो आप कुछ इस तरह लिख सकते हैं:
struct Connection
{
int type;
union
{
struct Network network;
struct USB usb;
struct Virtual virtual;
}
};
उदारीकरण में उदाहरण का उपयोग करें: http://git.0x539.de/?p=infinote.git=a=blob;f=libinfinity/common/inf-session.c;h=3e887870d63bd754c6b5ec232948027bbff4dc=hb=b;
यूनियन डेटा सदस्यों को अनुमति देते हैं जो समान मेमोरी साझा करने के लिए पारस्परिक रूप से अनन्य हैं। यह काफी महत्वपूर्ण है जब मेमोरी अधिक दुर्लभ होती है, जैसे कि एम्बेडेड सिस्टम।
निम्नलिखित उदाहरण में:
union {
int a;
int b;
int c;
} myUnion;
यह संघ 3 अलग-अलग इंट वैल्यू के बजाय एक ही इंट का स्थान लेगा। यदि उपयोगकर्ता a का मान सेट करता है , और फिर b का मान निर्धारित करता है , तो यह मान को अधिलेखित कर देगा एक के बाद से वे दोनों एक ही स्मृति स्थान साझा कर रहे हैं।
हमें बहुत सारे। बस करो grep union /usr/include/*
या समान निर्देशिकाओं में। अधिकांश मामलों में union
लपेटा जाता है struct
और संरचना का एक सदस्य बताता है कि संघ में किस तत्व को एक्सेस करना है। उदाहरण के man elf
लिए वास्तविक जीवन कार्यान्वयन के लिए चेकआउट ।
यह मूल सिद्धांत है:
struct _mydata {
int which_one;
union _data {
int a;
float b;
char c;
} foo;
} bar;
switch (bar.which_one)
{
case INTEGER : /* access bar.foo.a;*/ break;
case FLOATING : /* access bar.foo.b;*/ break;
case CHARACTER: /* access bar.foo.c;*/ break;
}
यहाँ अपने स्वयं के कोडबेस से एक संघ का उदाहरण है (स्मृति से और पैराफ़्रासेड से ताकि यह सटीक न हो)। इसका उपयोग भाषा के तत्वों को मेरे द्वारा बनाए गए दुभाषिया में संग्रहीत करने के लिए किया गया था। उदाहरण के लिए, निम्न कोड:
set a to b times 7.
निम्नलिखित भाषा तत्व शामिल हैं:
भाषा तत्व #define
इस प्रकार 'मान' के रूप में परिभाषित होते हैं :
#define ELEM_SYM_SET 0
#define ELEM_SYM_TO 1
#define ELEM_SYM_TIMES 2
#define ELEM_SYM_FULLSTOP 3
#define ELEM_VARIABLE 100
#define ELEM_CONSTANT 101
और निम्नलिखित संरचना का उपयोग प्रत्येक तत्व को संग्रहीत करने के लिए किया गया था:
typedef struct {
int typ;
union {
char *str;
int val;
}
} tElem;
तब प्रत्येक तत्व का आकार अधिकतम संघ का आकार था (टाइप के लिए 4 बाइट्स और संघ के लिए 4 बाइट्स, हालांकि वे विशिष्ट मान हैं, वास्तविक आकार कार्यान्वयन पर निर्भर करते हैं)।
"सेट" तत्व बनाने के लिए, आप उपयोग करेंगे:
tElem e;
e.typ = ELEM_SYM_SET;
"वैरिएबल [b]" एलिमेंट बनाने के लिए, आप उपयोग करेंगे:
tElem e;
e.typ = ELEM_VARIABLE;
e.str = strdup ("b"); // make sure you free this later
"निरंतर [7]" तत्व बनाने के लिए, आप उपयोग करेंगे:
tElem e;
e.typ = ELEM_CONSTANT;
e.val = 7;
और आप आसानी से फ़्लोट्स ( float flt
) या तर्कसंगत ( struct ratnl {int num; int denom;}
) और अन्य प्रकारों को शामिल करने के लिए इसका विस्तार कर सकते हैं ।
मूल आधार यह है कि str
और val
नहीं स्मृति में सन्निहित, वे वास्तव में ओवरलैप, तो यह जहां संरचना स्मृति स्थान पर आधारित है स्मृति का एक ही खंड पर एक अलग दृष्टिकोण हो रही है, यहाँ दिखाया गया है, का एक तरीका है कर रहे हैं 0x1010
और पूर्णांकों और संकेत दिए गए दोनों कर रहे हैं 4 निवाले:
+-----------+
0x1010 | |
0x1011 | typ |
0x1012 | |
0x1013 | |
+-----+-----+
0x1014 | | |
0x1015 | str | val |
0x1016 | | |
0x1017 | | |
+-----+-----+
यदि यह सिर्फ एक संरचना में होता है, तो यह इस तरह दिखेगा:
+-------+
0x1010 | |
0x1011 | typ |
0x1012 | |
0x1013 | |
+-------+
0x1014 | |
0x1015 | str |
0x1016 | |
0x1017 | |
+-------+
0x1018 | |
0x1019 | val |
0x101A | |
0x101B | |
+-------+
make sure you free this later
निरंतर तत्व से टिप्पणी को हटाया जाना चाहिए ?
मैं कहूंगा कि इससे मेमोरी को पुन: उपयोग करना आसान हो जाता है जिसका उपयोग विभिन्न तरीकों से किया जा सकता है, अर्थात मेमोरी को सेव करना। उदाहरण के लिए, आप कुछ "वैरिएंट" संरचना करना चाहेंगे जो एक छोटी स्ट्रिंग और साथ ही एक संख्या को बचाने में सक्षम है:
struct variant {
int type;
double number;
char *string;
};
32 बिट सिस्टम में इसके परिणामस्वरूप कम से कम 96 बिट्स या 12 बाइट्स का उपयोग किया जाएगा variant
।
एक संघ का उपयोग करके आप आकार को 64 बिट्स या 8 बाइट्स तक कम कर सकते हैं:
struct variant {
int type;
union {
double number;
char *string;
} value;
};
यदि आप अधिक भिन्न प्रकार के वेरिएंट्स जोड़ना चाहते हैं, तो आप इसे और अधिक सहेजने में सक्षम हैं। यह सच हो सकता है, कि आप शून्य पॉइंटर को कास्टिंग करने वाले समान काम कर सकते हैं - लेकिन संघ इसे और अधिक सुलभ बनाने के साथ-साथ टाइप भी करता है। सुरक्षित। इस तरह की बचत बड़े पैमाने पर नहीं लगती है, लेकिन आप इस संरचना के सभी उदाहरणों के लिए उपयोग की जाने वाली स्मृति का एक तिहाई बचा रहे हैं।
एक विशिष्ट अवसर के बारे में सोचना मुश्किल है जब आपको इस प्रकार की लचीली संरचना की आवश्यकता होगी, शायद एक संदेश प्रोटोकॉल में जहां आप विभिन्न प्रकार के संदेश भेज रहे होंगे, लेकिन तब भी शायद बेहतर और अधिक प्रोग्रामर अनुकूल विकल्प हैं।
यूनियन्स अन्य भाषाओं में भिन्न प्रकार के होते हैं - वे एक समय में केवल एक चीज पकड़ सकते हैं, लेकिन यह बात एक उदाहरण, एक फ्लोट आदि हो सकती है, जो इस बात पर निर्भर करता है कि आप इसे कैसे घोषित करते हैं।
उदाहरण के लिए:
typedef union MyUnion MYUNION;
union MyUnion
{
int MyInt;
float MyFloat;
};
MyUnion में केवल एक int या एक फ़्लोट होगा, जिसके आधार पर आप सबसे हाल ही में सेट होते हैं । तो यह कर:
MYUNION u;
u.MyInt = 10;
यू अब 10 के बराबर एक इंट रखती है;
u.MyFloat = 1.0;
u अब 1.0 के बराबर एक फ्लोट रखता है। यह अब कोई इंट नहीं रखती है। जाहिर है अब अगर आप कोशिश करते हैं और प्रिंटफ करते हैं ("MyInt =% d", u.MyInt); तब आप शायद एक त्रुटि प्राप्त करने जा रहे हैं, हालांकि मैं विशिष्ट व्यवहार के बारे में अनिश्चित हूं।
संघ का आकार उसके सबसे बड़े क्षेत्र के आकार से तय होता है, इस मामले में फ्लोट।
sizeof(int) == sizeof(float)
( == 32
) आमतौर पर।
जब आप हार्डवेयर, डिवाइस या नेटवर्क प्रोटोकॉल द्वारा परिभाषित मॉडल को मॉडल करना चाहते हैं, या जब आप बड़ी संख्या में ऑब्जेक्ट बना रहे हैं और अंतरिक्ष को बचाना चाहते हैं, तो यूनियनों का उपयोग किया जाता है। आपको वास्तव में उन्हें 95% समय की आवश्यकता नहीं है, हालांकि आसान-से-डिबग कोड के साथ रहना है।
इनमें से कई जवाब एक प्रकार से दूसरे प्रकार के कास्टिंग से संबंधित हैं। मैं यूनियनों से सबसे अधिक उपयोग केवल उसी प्रकार से करता हूं (जैसे जब एक धारावाहिक डेटा स्ट्रीम पार्स करते हैं)। वे एक फ़्रेमयुक्त पैकेट के पार्सिंग / निर्माण को तुच्छ बनने देते हैं।
typedef union
{
UINT8 buffer[PACKET_SIZE]; // Where the packet size is large enough for
// the entire set of fields (including the payload)
struct
{
UINT8 size;
UINT8 cmd;
UINT8 payload[PAYLOAD_SIZE];
UINT8 crc;
} fields;
}PACKET_T;
// This should be called every time a new byte of data is ready
// and point to the packet's buffer:
// packet_builder(packet.buffer, new_data);
void packet_builder(UINT8* buffer, UINT8 data)
{
static UINT8 received_bytes = 0;
// All range checking etc removed for brevity
buffer[received_bytes] = data;
received_bytes++;
// Using the struc only way adds lots of logic that relates "byte 0" to size
// "byte 1" to cmd, etc...
}
void packet_handler(PACKET_T* packet)
{
// Process the fields in a readable manner
if(packet->fields.size > TOO_BIG)
{
// handle error...
}
if(packet->fields.cmd == CMD_X)
{
// do stuff..
}
}
संपादन एंडियननेस और स्ट्रक्चर पैडिंग के बारे में टिप्पणी मान्य, और महान, चिंताएं हैं। मैंने कोड के इस शरीर का उपयोग लगभग पूरी तरह से एम्बेडेड सॉफ़्टवेयर में किया है, जिसमें से अधिकांश में पाइप के दोनों सिरों का नियंत्रण था।
यूनियनों महान हैं। यूनियनों का एक चतुर उपयोग मैंने देखा है कि किसी घटना को परिभाषित करते समय उनका उपयोग करना है। उदाहरण के लिए, आप यह तय कर सकते हैं कि कोई घटना 32 बिट्स की है।
अब, उस 32 बिट्स के भीतर, आप पहले 8 बिट्स को डिज़ाइन करना पसंद कर सकते हैं जैसे कि घटना के प्रेषक की पहचान करने वाले के लिए ... कभी-कभी आप पूरी घटना के रूप में डील करते हैं, कभी-कभी आप इसे विच्छेदित करते हैं और इसके घटकों की तुलना करते हैं। यूनियनों ने आपको दोनों करने की छूट दी।
संघ घटना { अहस्ताक्षरित लंबा ईवेंटकोड; अहस्ताक्षरित चारपार्टपार्ट्स [4]; };
व्हाट अबाउट VARIANT
COM इंटरफेस में उपयोग किए जाने वाले है? इसके दो क्षेत्र हैं - "प्रकार" और एक वास्तविक मूल्य रखने वाला संघ जो "प्रकार" फ़ील्ड के आधार पर व्यवहार किया जाता है।
जब मैं एम्बेडेड उपकरणों के लिए कोडिंग कर रहा था तो मैंने यूनियन का उपयोग किया। मेरे पास C int है जो 16 बिट लंबा है। और मुझे उच्च 8 बिट्स और निचले 8 बिट्स को पुनः प्राप्त करने की आवश्यकता है, जब मुझे EEPROM / स्टोर से पढ़ने की आवश्यकता है। तो मैंने इस तरह से इस्तेमाल किया:
union data {
int data;
struct {
unsigned char higher;
unsigned char lower;
} parts;
};
इसे शिफ्टिंग की आवश्यकता नहीं है इसलिए कोड को पढ़ना आसान है।
दूसरी ओर, मैंने कुछ पुराने C ++ stl कोड देखे जो stl एलोकेटर के लिए यूनियन का उपयोग करते थे। यदि आप रुचि रखते हैं, तो आप sgi stl स्रोत कोड पढ़ सकते हैं । यहाँ इसका एक टुकड़ा है:
union _Obj {
union _Obj* _M_free_list_link;
char _M_client_data[1]; /* The client sees this. */
};
struct
अपने आसपास एक समूहीकरण की आवश्यकता नहीं होगी higher
/ lower
? अभी दोनों को पहले बाइट की ओर ही इशारा करना चाहिए।
इस पर एक नज़र डालें: X.25 बफर कमांड हैंडलिंग
कई संभावित X.25 आदेशों में से एक को एक बफर में प्राप्त किया जाता है और सभी संभावित संरचनाओं के एक संघ का उपयोग करके नियंत्रित किया जाता है।
सी के शुरुआती संस्करणों में, सभी संरचना घोषणाएं खेतों के एक सामान्य सेट को साझा करती हैं। दिया हुआ:
struct x {int x_mode; int q; float x_f};
struct y {int y_mode; int q; int y_l};
struct z {int z_mode; char name[20];};
एक संकलक अनिवार्य रूप से संरचनाओं के आकार (और संभवतः संरेखण) की एक तालिका का उत्पादन करेगा, और संरचनाओं के सदस्यों के नाम, प्रकार और ऑफसेट की एक अलग तालिका। संकलक ने यह नहीं बताया कि कौन से सदस्य किस संरचना से संबंधित हैं, और दो संरचनाओं को एक ही नाम के साथ एक सदस्य रखने की अनुमति देगा यदि प्रकार और ऑफसेट मिलान के रूप में ( और सदस्य q
के रूप में)struct x
struct y
)। यदि p किसी भी संरचना प्रकार के लिए एक संकेतक था, p-> q सूचक p में "q" की ऑफसेट जोड़ देगा और परिणामी पते से एक "int" प्राप्त करेगा।
उपर्युक्त शब्दार्थ को देखते हुए, एक ऐसा फ़ंक्शन लिखना संभव था जो कई प्रकार के संरचना पर परस्पर उपयोगी तरीके से कुछ उपयोगी कार्य कर सके, बशर्ते कि फ़ंक्शन द्वारा उपयोग किए जाने वाले सभी फ़ील्ड प्रश्न में संरचनाओं के भीतर उपयोगी फ़ील्ड्स के साथ पंक्तिबद्ध हों। यह एक उपयोगी विशेषता थी, और सी को मान्य करने के लिए बदलते संरचना के प्रकारों के खिलाफ उपयोग किए गए सदस्यों को प्रश्न में संरचनाओं के प्रकार के लिए इसका मतलब होगा कि एक संरचना होने के साधन के अभाव में इसे खो सकते हैं जिसमें एक ही पते पर कई नामित फ़ील्ड शामिल हो सकते हैं। C में "यूनियन" प्रकार जोड़ने से उस अंतर को कुछ हद तक भरने में मदद मिली (हालांकि, IMHO, साथ ही साथ यह नहीं होना चाहिए)।
यूनियनों की उस खाई को भरने की क्षमता का एक अनिवार्य हिस्सा यह तथ्य था कि एक संघ के सदस्य के लिए एक सूचक को किसी भी संघ में एक सूचक में परिवर्तित किया जा सकता है, और किसी भी संघ के एक सूचक को किसी भी सदस्य के लिए एक सूचक में परिवर्तित किया जा सकता है। C89 स्टैंडर्ड स्पष्ट रूप से यह नहीं कहा है कि एक कास्टिंग T*
एक करने के लिए सीधे U*
एक सूचक को यह किसी भी संघ प्रकार के कास्टिंग दोनों युक्त के बराबर था T
और U
, और फिर कास्टिंग कि करने के लिए U*
, बाद डाली अनुक्रम का कोई परिभाषित व्यवहार से प्रभावित होंगे संघ प्रकार का उपयोग किया है, और मानक से एक सीधा कलाकारों के लिए किसी भी विपरीत अर्थ विज्ञान निर्दिष्ट नहीं किया है T
करने के लिए U
। इसके अलावा, ऐसे मामलों में जहां एक फ़ंक्शन को अज्ञात मूल का सूचक मिला, एक के माध्यम ऑब्जेक्ट लिखने का व्यवहारT*
, परिवर्तित करनाT*
केU*
, और फिर ऑब्जेक्ट को पढ़ना U*
टाइप के सदस्य के माध्यम से एक यूनियन लिखने T
और टाइप के रूप में पढ़ने के बराबर होगा U
, जो कुछ मामलों में मानक-परिभाषित होगा (उदाहरण के लिए जब कॉमन इनिशियल सीक्वेंस मेंबर्स तक पहुंच हो) और इंप्लीमेंट-डिफाइंड (अंडरडाइंड के बजाय) ) शेष के लिए। हालांकि, कार्यक्रमों के लिए यह दुर्लभ था कि यूनियन प्रकार की वास्तविक वस्तुओं के साथ सीआईएस गारंटी का फायदा उठाया जाए, इस तथ्य का फायदा उठाना कहीं अधिक सामान्य था कि अज्ञात मूल की वस्तुओं को पॉइंटर्स की तरह यूनियन सदस्यों को व्यवहार करना पड़ता था और इसके साथ संबंधित व्यवहार की गारंटी होती थी।
foo
एक है int
के साथ ऑफसेट 8, तो anyPointer->foo = 1234;
मतलब था "anyPointer में पता 8 बाइट्स से यह विस्थापित लेते हैं,, और जिसके परिणामस्वरूप पता करने के लिए 1234 मूल्य के एक पूर्णांक की दुकान करते हैं। संकलक है कि क्या जानते हैं या देखभाल की जरूरत नहीं होगी anyPointer
पहचान किसी भी प्रकार की संरचना जो foo
उसके सदस्यों के बीच सूचीबद्ध थी ।
anyPointer
एक स्ट्रक्चर मेंबर के साथ इंडेंटिफाई होता है, तो कंपाइलर to have a member with the same name only if the type and offset matched
आपके पोस्ट की इन कंडीशन को कैसे चेक करेगा ?
p->foo
प्रकार और ऑफसेट पर निर्भर करेगा foo
। अनिवार्य रूप से, p->foo
शॉर्टहैंड था *(typeOfFoo*)((unsigned char*)p + offsetOfFoo)
। आपके उत्तरार्द्ध प्रश्न के लिए, जब एक कंपाइलर एक संरचनात्मक सदस्य परिभाषा का सामना करता है, तो यह आवश्यक है कि या तो उस नाम के साथ कोई सदस्य मौजूद नहीं है, या उस नाम वाले सदस्य के पास एक ही प्रकार और ऑफसेट है; मुझे लगता है कि अगर एक गैर-मिलान संरचना सदस्य परिभाषा मौजूद है, तो स्क्वाक्क्ड होगा, लेकिन मुझे नहीं पता कि यह कैसे त्रुटियों को नियंत्रित करता है।
एक सरल और बहुत उपयोगी उदाहरण है, ....
कल्पना कीजिए:
आपके पास एक है uint32_t array[2]
और बाइट श्रृंखला के तीसरे और चौथे बाइट का उपयोग करना चाहते हैं। आप कर सकते हैं *((uint16_t*) &array[1])
। लेकिन यह सख्त रूप से सख्त अलियासिंग नियमों को तोड़ता है!
लेकिन ज्ञात संकलक आपको निम्न कार्य करने की अनुमति देते हैं:
union un
{
uint16_t array16[4];
uint32_t array32[2];
}
तकनीकी रूप से यह अभी भी नियमों का उल्लंघन है। लेकिन सभी ज्ञात मानक इस उपयोग का समर्थन करते हैं।