C / C ++: Force Bit Field Order और Alignment


87

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

struct Message
{
  unsigned int version : 3;
  unsigned int type : 1;
  unsigned int id : 5;
  unsigned int data : 6;
} __attribute__ ((__packed__));

जीसीसी संकलक के साथ एक इंटेल प्रोसेसर पर, मेमोरी दिखाए जाने पर फ़ील्ड्स को स्मृति में रखा गया था। Message.versionबफर में पहले 3 बिट्स थे, और Message.typeउसके बाद। यदि मुझे विभिन्न कंपाइलरों के लिए समान संरचना पैकिंग विकल्प मिलें, तो क्या यह क्रॉस-प्लेटफ़ॉर्म होगा?


17
चूंकि एक बफर बाइट्स का एक सेट है, न कि बिट्स, "बफर में पहले 3 बिट्स" एक सटीक अवधारणा नहीं है। क्या आप पहले बाइट के 3 निम्नतम-ऑर्डर बिट्स को पहले 3 बिट्स या 3 उच्चतम-ऑर्डर बिट्स मानेंगे?
कैफ़े

2
नेटवर्क पर पारगमन करते समय, "बफर में पहले 3 बिट्स" बहुत अच्छी तरह से परिभाषित हो जाते हैं।
जोशुआ

2
@ जोशुआ IIRC, ईथरनेट सबसे पहले प्रत्येक बाइट के सबसे कम-महत्वपूर्ण बिट को प्रसारित करता है (यही कारण है कि प्रसारण बिट जहां यह है)।
टीसी

जब आप "पोर्टेबल" और "क्रॉस-प्लेटफॉर्म" कहते हैं, तो आपका क्या मतलब है? निष्पादन योग्य लक्ष्य ओएस की परवाह किए बिना आदेश को सही ढंग से एक्सेस करेगा - या - टूलचैन की परवाह किए बिना कोड संकलित करेगा?
Garet Claborn

जवाबों:


103

नहीं, यह पूरी तरह से पोर्टेबल नहीं होगा। संरचनाओं के लिए पैकिंग विकल्प एक्सटेंशन हैं, और स्वयं पूरी तरह से पोर्टेबल नहीं हैं। इसके अलावा, C99 §6.7.2.1, पैराग्राफ 10 कहता है: "एक इकाई के भीतर बिट-फ़ील्ड के आवंटन का आदेश (उच्च-ऑर्डर से कम-ऑर्डर या उच्च-ऑर्डर करने के लिए कम-ऑर्डर) कार्यान्वयन-परिभाषित है।"

उदाहरण के लिए, एक एकल कंपाइलर लक्ष्य प्लेटफ़ॉर्म की समाप्ति के आधार पर बिट क्षेत्र को अलग तरीके से रख सकता है।


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

10
C मानक ने बिट फ़ील्ड के लिए आदेश की गारंटी क्यों नहीं दी?
आरोन कैंपबेल

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

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

करता है packed: आदेश देने को लागू stackoverflow.com/questions/1756811/... बिट आदेश लागू करने के लिए कैसे: stackoverflow.com/questions/6728218/gcc-compiler-bit-order
सिरो Santilli郝海东冠状病六四事件法轮功

45

बिट फ़ील्ड कंपाइलर से कंपाइलर, सॉरी तक व्यापक रूप से भिन्न होते हैं।

जीसीसी के साथ, बड़ी एंडियन मशीनें पहले बिट्स एंड एंड बिट एंडियन मशीन लेट बिट्स एंड आउट लेट बिट्स एंड लेट आउट लेट आउट करती हैं।

K & R का कहना है कि "आसन्न [बिट-] संरचनाओं के क्षेत्र के सदस्यों को कार्यान्वयन-निर्भर दिशा में कार्यान्वयन-निर्भर भंडारण इकाइयों में पैक किया जाता है। जब किसी अन्य क्षेत्र का अनुसरण करने वाला क्षेत्र फिट नहीं होगा ... तो यह इकाइयों या इकाई के बीच विभाजित हो सकता है। गद्देदार। चौड़ाई का एक अनाम क्षेत्र 0 इस गद्दी को बाध्य करता है ... "

इसलिए, यदि आपको मशीन स्वतंत्र बाइनरी लेआउट की आवश्यकता है, तो आपको इसे स्वयं करना होगा।

यह अंतिम कथन पैडिंग के कारण गैर-बिटफ़िल्ड पर भी लागू होता है - हालाँकि सभी कंपाइलरों को किसी संरचना की बाइट पैकिंग के लिए मजबूर करने के कुछ तरीके प्रतीत होते हैं, जैसा कि मैं आपको जीसीसी के लिए पहले से ही खोज रहा हूँ।


क्या K & R को वास्तव में एक उपयोगी संदर्भ माना जाता है, यह देखते हुए कि यह पूर्व-मानकीकरण था और (मुझे लगता है?) शायद कई क्षेत्रों में डूब गया है।
अंडरस्कोर_ड

1
मेरा K & R पोस्ट-एएनएसआई है।
जोशुआ

1
अब यह शर्मनाक है: मुझे नहीं पता था कि उन्होंने पोस्ट-एएनएसआई संशोधन जारी किया था। मेरी गलती!
अंडरस्कोर_ड

35

बिटफ़िल्ड से बचा जाना चाहिए - वे एक ही मंच के लिए भी संकलक के बीच बहुत पोर्टेबल नहीं हैं। C99 मानक 6.7.2.1/10 से - "संरचना और संघ विनिर्देशक" (C90 मानक में ऐसा ही शब्द है):

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

आप इस बात की गारंटी नहीं दे सकते हैं कि एक बिट क्षेत्र एक अंतर सीमा को 'स्पैन' करेगा या नहीं और आप यह निर्दिष्ट नहीं कर सकते हैं कि कोई बिटफील्ड इंट के निचले छोर पर शुरू होता है या इंट के उच्च अंत में (यह स्वतंत्र है कि प्रोसेसर क्या है) बड़ा-एंडियन या थोड़ा-सा एंडियन)।

बिटमास्क को प्राथमिकता दें। बिट्स सेट, स्पष्ट और परीक्षण करने के लिए इनलाइन (या यहां तक ​​कि मैक्रोज़) का उपयोग करें।


2
संकलन के समय बिटफिल्ड का क्रम निर्धारित किया जा सकता है।
ग्रेग ए। वुड्स

9
इसके अलावा, बिटफ़िल्ड्स को बहुत अधिक पसंद किया जाता है जब बिट फ़्लैग के साथ काम किया जाता है, जिसका प्रोग्राम के बाहर कोई बाहरी प्रतिनिधित्व नहीं होता है (अर्थात डिस्क पर या रजिस्टरों में या अन्य कार्यक्रमों द्वारा एक्सेस की गई मेमोरी में, आदि)।
ग्रेग ए। वुड्स

1
@ ग्रेगै। वूड्स: यदि यह वास्तव में मामला है, तो कृपया यह वर्णन करने का उत्तर दें कि कैसे। मुझे कुछ भी नहीं मिला लेकिन आपकी टिप्पणी जब इसके लिए
गुगली

1
@ ग्रेगा। वूड्स: क्षमा करें, मुझे किस टिप्पणी के लिए लिखा जाना चाहिए था। मेरा मतलब था: आप कहते हैं कि "संकलन के समय संकलन का क्रम निर्धारित किया जा सकता है।" मैं इसके बारे में कुछ नहीं कर सकता और इसे कैसे करना है।
11

2
@mozzbozz planix.com/~woods/projects/wsg2000.c पर एक नज़र डालें और परिभाषाएँ और इसके उपयोग के लिए खोजें_BIT_FIELDS_LTOH_BIT_FIELDS_HTOL
ग्रेग ए। वुड्स

11

एंडियननेस बाइट ऑर्डर के बारे में बात कर रहे हैं बिट ऑर्डर नहीं। आजकल , यह 99% सुनिश्चित है कि बिट ऑर्डर तय हो गए हैं। हालांकि, बिटफिल्ड्स का उपयोग करते समय, धीरज को गिनती में लिया जाना चाहिए। नीचे दिए गए उदाहरण देखें।

#include <stdio.h>

typedef struct tagT{

    int a:4;
    int b:4;
    int c:8;
    int d:16;
}T;


int main()
{
    char data[]={0x12,0x34,0x56,0x78};
    T *t = (T*)data;
    printf("a =0x%x\n" ,t->a);
    printf("b =0x%x\n" ,t->b);
    printf("c =0x%x\n" ,t->c);
    printf("d =0x%x\n" ,t->d);

    return 0;
}

//- big endian :  mips24k-linux-gcc (GCC) 4.2.3 - big endian
a =0x1
b =0x2
c =0x34
d =0x5678
 1   2   3   4   5   6   7   8
\_/ \_/ \_____/ \_____________/
 a   b     c           d

// - little endian : gcc (Ubuntu 4.3.2-1ubuntu11) 4.3.2
a =0x2
b =0x1
c =0x34
d =0x7856
 7   8   5   6   3   4   1   2
\_____________/ \_____/ \_/ \_/
       d           c     b   a

6
A और b का आउटपुट बताता है कि एंडियननेस अभी भी बिट ऑर्डर और बाइट ऑर्डर के बारे में बात कर रहा है।
विंडोज प्रोग्रामर

बिट क्रम और बाइट ऑर्डर की समस्याग्रस्तताओं के साथ अद्भुत उदाहरण
जोनाथन

1
क्या आपने वास्तव में कोड को संकलित और चलाया था? "ए" और "बी" के मूल्य मेरे लिए तर्कसंगत नहीं लगते हैं: आप मूल रूप से कह रहे हैं कि संकलक एंडियन के कारण बाइट के भीतर निबल्स को स्वैप करेगा। "डी" के मामले में, एंडियन को चार सरणियों के भीतर बाइट क्रम को प्रभावित नहीं करना चाहिए (मान लें कि चार 1 बाइट लंबा है); यदि कंपाइलर ने ऐसा किया है, तो हम पॉइंटर्स का उपयोग करके एक सरणी के माध्यम से पुनरावृति नहीं कर पाएंगे। यदि, दूसरी ओर आपने दो 16 बिट पूर्णांक की एक सरणी का उपयोग किया है जैसे: uint16 डेटा [] = {0x1234,0x5678}; तब d निश्चित रूप से थोड़ा एंडियन सिस्टम में 0x7856 होगा।
क्रस

6

ज्यादातर समय, शायद, लेकिन इस पर खेत को दांव न लगाएं, क्योंकि यदि आप गलत हैं, तो आप बड़ा खो देंगे।

यदि आप वास्तव में, वास्तव में समान बाइनरी जानकारी की आवश्यकता है, तो आपको बिटमास्क के साथ बिटफ़िल्ड बनाने की आवश्यकता होगी - जैसे आप संदेश के लिए एक अहस्ताक्षरित शॉर्ट (16 बिट) का उपयोग करते हैं, और फिर तीन टॉप बिट्स का प्रतिनिधित्व करने के लिए versionMask = 0xE000 जैसी चीजें बनाते हैं।

संरचना के भीतर संरेखण के साथ एक समान समस्या है। उदाहरण के लिए, स्पार्क, पावरपीसी और 680x0 सीपीयू सभी बड़े-एंडियन हैं, और स्पार्क और पावरपीसी कंपाइलर्स के लिए सामान्य डिफ़ॉल्ट 4-बाइट सीमाओं पर संरचना सदस्यों को संरेखित करना है। हालाँकि, एक संकलक मैंने 680x0 के लिए केवल 2-बाइट सीमाओं पर संरेखित किया था - और संरेखण को बदलने का कोई विकल्प नहीं था!

तो कुछ संरचनाओं के लिए, स्पार्क और पावरपीसी पर आकार समान हैं, लेकिन 680x0 पर छोटे हैं, और कुछ सदस्य संरचना के भीतर अलग-अलग मेमोरी ऑफ़सेट में हैं।

यह मेरे द्वारा काम की गई एक परियोजना के साथ एक समस्या थी, क्योंकि स्पार्क पर चल रही एक सर्वर प्रक्रिया एक ग्राहक को क्वेरी करेगी और पता चला कि यह बड़ा-एंडियन था, और यह मान लें कि यह नेटवर्क पर केवल द्विआधारी संरचना को निचोड़ सकता है और क्लाइंट सामना कर सकता है। और वह PowerPC क्लाइंट पर ठीक काम करता है, और 680x0 क्लाइंट पर बड़ा समय दुर्घटनाग्रस्त हो गया। मैंने कोड नहीं लिखा था, और समस्या को खोजने में काफी समय लगा। लेकिन एक बार करने के बाद इसे ठीक करना आसान था।


1

धन्यवाद @BenVoigt आपकी बहुत उपयोगी टिप्पणी शुरू करने के लिए

नहीं, वे स्मृति को बचाने के लिए बनाए गए थे।

लिनक्स स्रोत बाहरी संरचना से मेल खाने के लिए एक छोटे से क्षेत्र का उपयोग करता है : /usr/include/linux/ip.h में IP डेटाग्राम के पहले बाइट के लिए यह कोड है

struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
        __u8    ihl:4,
                version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
        __u8    version:4,
                ihl:4;
#else
#error  "Please fix <asm/byteorder.h>"
#endif

हालाँकि आपकी टिप्पणी के आलोक में मैं इसे मल्टी-बाइट बिट फिल्ड frag_off के लिए काम करने की कोशिश कर रहा हूँ ।


-9

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


5
मुझे लगता है कि यह
बताना

13
@trondd: नहीं, वे स्मृति को बचाने के लिए बनाए गए थे। Bitfields का इरादा बाहरी डेटा संरचनाओं, जैसे मेमोरी-मैप्ड हार्डवेयर रजिस्टरों, नेटवर्क प्रोटोकॉल, या फ़ाइल स्वरूपों में मैप करने के लिए नहीं है। यदि उन्हें बाहरी डेटा संरचनाओं में मैप करने का इरादा था, तो पैकिंग ऑर्डर को मानकीकृत किया गया होगा।
बेन वोइगट

2
बिट्स का उपयोग करने से मेमोरी की बचत होती है। बिट फ़ील्ड का उपयोग करने से पठनीयता बढ़ती है। कम मेमोरी का उपयोग तेज है। बिट्स का उपयोग करना अधिक जटिल परमाणु संचालन के लिए अनुमति देता है। वास्तविक दुनिया में बाहर के अनुप्रयोगों में, प्रदर्शन और जटिल परमाणु संचालन की आवश्यकता होती है। यह जवाब हमारे लिए काम नहीं करेगा।
जॉन्नीक्रैश

@BenVoigt शायद सच है, लेकिन अगर कोई प्रोग्रामर यह पुष्टि करने के लिए तैयार है कि उनके कंपाइलर / ABI का ऑर्डर उनकी ज़रूरतों से मेल खाता है, और उसी के अनुसार जल्दी पोर्टेबिलिटी का त्याग करें - तो वे निश्चित रूप से उस भूमिका को पूरा कर सकते हैं । 9 * के रूप में, "वास्तविक दुनिया कोडर" के कौन से आधिकारिक जन बिटफ़िल्ड के सभी उपयोग को "अव्यवसायिक / आलसी / बेवकूफ" मानते हैं और उन्होंने यह कहां से लिखा है?
अंडरस्कोर_ड

2
कम मेमोरी का उपयोग करना हमेशा तेज नहीं होता है; अधिक मेमोरी का उपयोग करने और पोस्ट-पढ़ने के संचालन को कम करने के लिए यह अक्सर अधिक कुशल होता है, और प्रोसेसर / प्रोसेसर मोड इसे और भी सच बना सकता है।
डेव न्यूटन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.