यह संरचना आकार 2 के बजाय 3 क्यों है?


91

मैंने इस संरचना को परिभाषित किया है:

typedef struct
{
    char A:3;
    char B:3;
    char C:3;
    char D:3;
    char E:3;
} col; 

sizeof(col)मुझे 3 के उत्पादन में देना नहीं, बल्कि यह 2 होना चाहिए? अगर मैं सिर्फ एक तत्व पर टिप्पणी करता हूं, तो sizeofयह है 2. मैं नहीं समझता कि क्यों: 3 बिट्स के पांच तत्व 15 बिट्स के बराबर हैं, और यह 2 बाइट्स से कम है।

क्या इस तरह की संरचना को परिभाषित करने में "आंतरिक आकार" है? मुझे बस एक स्पष्टीकरण की आवश्यकता है, क्योंकि भाषा की मेरी धारणा से अब तक, मुझे 2 बाइट के आकार की उम्मीद थी, 3 की नहीं।


4
यह शायद संरेखण का अनुकूलन है। यह एक नया बाइट शुरू करता है, अगर अगला बिट आकार वास्तविक कब्जे वाले स्थान में फिट नहीं होगा।
atν 13α

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

3
ध्यान दें कि C के लिए, int का उपयोग करने की तुलना में char का उपयोग करना कम पोर्टेबल है, stackoverflow.com/a/23987436/23118
hlovdal

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

3
विशेष रूप से, C99 में, §6.7.2.1 Struct और संघ विनिर्देशक, ¶4 थोड़ा-क्षेत्र एक प्रकार का एक योग्य या अयोग्य संस्करण है कि होगा _Bool, signed int, unsigned int, या कुछ अन्य कार्यान्वयन से परिभाषित प्रकार। charइसलिए उपयोग करना 'अन्य कार्यान्वयन-परिभाषित प्रकार' श्रेणी में आता है।
जोनाथन लेफ़लर

जवाबों:


95

क्योंकि आप charअपने खेतों के लिए अंतर्निहित प्रकार के रूप में उपयोग कर रहे हैं , संकलक बाइट्स द्वारा समूह बिट्स की कोशिश करता है, और चूंकि यह प्रत्येक बाइट में आठ बिट्स से अधिक नहीं डाल सकता है, यह केवल प्रति बाइट दो फ़ील्ड संग्रहीत कर सकता है।

बिट्स का कुल योग आपकी संरचना का उपयोग करता है 15, इसलिए आदर्श आकार फिट करने के लिए कि बहुत अधिक डेटा होगा short

#include <stdio.h>

typedef struct
{
  char A:3;
  char B:3;
  char C:3;
  char D:3;
  char E:3;
} col; 


typedef struct {
  short A:3;
  short B:3;
  short C:3;
  short D:3;
  short E:3;
} col2; 


int main(){

  printf("size of col: %lu\n", sizeof(col));
  printf("size of col2: %lu\n", sizeof(col2));

}

उपरोक्त कोड (मेरा जैसा 64-बिट प्लेटफ़ॉर्म के लिए) वास्तव 2में दूसरी संरचना के लिए होगा। एक से अधिक किसी भी चीज के लिए short, संरचना उपयोग किए गए प्रकार के एक से अधिक तत्वों को नहीं भरेगी, इसलिए - उसी प्लेटफॉर्म के लिए - संरचना चार के लिए आकार के साथ समाप्त होगी int, आठ के लिए long, आदि।


1
प्रस्तावित संरचनात्मक परिभाषा अभी भी गलत है। सही संरचना परिभाषा 'अहस्ताक्षरित लघु' का उपयोग करेगी।
user3629249

21
@ user3629249 अहस्ताक्षरित लघु 'सही' क्यों है? अगर यूजर -4 से 3 तक स्टोर करना चाहता है तो शॉर्ट सही है। यदि उपयोगकर्ता 0 से 7 तक स्टोर करना चाहता है तो अहस्ताक्षरित शॉर्ट सही है। मूल प्रश्न एक हस्ताक्षरित प्रकार का उपयोग करता है, लेकिन मैं यह नहीं बता सकता कि क्या यह जानबूझकर या आकस्मिक था।
ब्रूस डावसन

2
बीट्वीन charऔर अंतर क्यों है short?
जिंजरप्लस 5

5
@ ब्रूसडसन: मानक को लागू करने की अनुमति देने के charलिए अहस्ताक्षरित ...
थॉमस Eding

@ThomasEding सच है, मानक चार को अहस्ताक्षरित करने की अनुमति देता है। लेकिन मेरा मुख्य बिंदु यह है कि यह दावा करने के लिए कोई कारण नहीं दिया गया कि अहस्ताक्षरित कमी सही थी (हालांकि यह आमतौर पर होगी)।
ब्रूस डॉसन

78

क्योंकि आपके पास एक छोटा पैकेट फ़ील्ड नहीं हो सकता है, जो न्यूनतम संरेखण सीमा (जो 1 बाइट) पर फैला हो, ताकि वे पैक की तरह मिल जाएं

byte 1
  A : 3
  B : 3
  padding : 2
byte 2
  C : 3
  D : 3
  padding : 2
byte 3
  E : 3
  padding : 5

(उसी बाइट के अंदर फील्ड / पैडिंग के आदेश जानबूझकर नहीं है, यह आपको केवल विचार देने के लिए है, क्योंकि कंपाइलर उन्हें नीचे रख सकता है कि यह कैसे पसंद करता है)


16

पहले दो बिट फ़ील्ड एक एकल में फिट होते हैं char। तीसरा उस में फिट नहीं हो सकता है charऔर उसे एक नया चाहिए। 3 + 3 + 3 = 9 जो 8 बिट में फिट नहीं होता है।

तो पहली जोड़ी एक लेती है char, दूसरी जोड़ी एक लेती है char, और अंतिम बिट क्षेत्र को तीसरा मिलता है char


15

अधिकांश संकलक आपको पैडिंग को नियंत्रित करने की अनुमति देते हैं, जैसे कि #pragmaएस का उपयोग करना । यहाँ जीसीसी 4.8.1 के साथ एक उदाहरण है:

#include <stdio.h>

typedef struct
{
    char A:3;
    char B:3;
    char C:3;
    char D:3;
    char E:3;
} col;

#pragma pack(push, 1)
typedef struct {
    char A:3;
    char B:3;
    char C:3;
    char D:3;
    char E:3;
} col2;
#pragma pack(pop)

int main(){
    printf("size of col: %lu\n", sizeof(col));  // 3
    printf("size of col2: %lu\n", sizeof(col2));  // 2
}

ध्यान दें कि संकलक का डिफ़ॉल्ट व्यवहार एक कारण के लिए है और संभवतः आपको बेहतर प्रदर्शन देगा।


9

भले ही ANSI C मानक इस बात के बारे में बहुत कम निर्दिष्ट करता है कि बिटफ़िल्ड को किसी भी महत्वपूर्ण लाभ की पेशकश करने के लिए कैसे पैक किया जाता है "कंपाइलर को बिटफ़िल्ड को पैक करने की अनुमति दी जाती है हालांकि वे फिट दिखते हैं", फिर भी कई मामलों में कंपाइलर को सबसे कुशल फैशन में चीजों को पैक करने से मना करते हैं।

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

unsigned char foo1: 3;
unsigned char foo2: 3;
unsigned char foo3: 3;
unsigned char foo4: 3;
unsigned char foo5: 3;
unsigned char foo6: 3;
unsigned char foo7: 3;

यदि unsigned char8 बिट्स हैं, तो कंपाइलर को उस प्रकार के चार फ़ील्ड आवंटित करने की आवश्यकता होगी, और सभी को दो बिटफ़िल्ड असाइन करें, लेकिन एक (जो charअपने स्वयं के क्षेत्र में होगा )। यदि सभी charघोषणाओं के साथ प्रतिस्थापित किया गया था short, तो दो प्रकार के क्षेत्र होंगे short, जिनमें से एक में पांच बिटफ़िल्ड होंगे और जिनमें से अन्य शेष दो को पकड़ेंगे।

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

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

unsigned short f1;
unsigned char f2;
union foo1 = f1:0.3;
union foo2 = f1:3.3;
union foo3 = f1:6.3;
union foo4 = f1:9.3;
union foo5 = f1:12.3;
union foo6 = f2:0.3;
union foo7 = f2:3.3;

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


2
अलग-अलग कंपाइलर अलग-अलग फील्ड को अलग-अलग तरीके से बिछाएंगे। मैंने विज़ुअल C ++ के लिए कुछ दस्तावेज लिखे जो कि प्रासंगिक हो सकते हैं। यह कष्टप्रद नुकसान की ओर इशारा करता है
ब्रूस डावसन

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