क्यों एक बिट क्षेत्र के लिए एक ही मूल्य वापस नहीं दे रहा है एक मूल्य प्रदान कर रहा है?


96

मैंने इस Quora पोस्ट में निम्न कोड देखा :

#include <stdio.h>

struct mystruct { int enabled:1; };
int main()
{
  struct mystruct s;
  s.enabled = 1;
  if(s.enabled == 1)
    printf("Is enabled\n"); // --> we think this to be printed
  else
    printf("Is disabled !!\n");
}

C और C ++ दोनों में, कोड का उत्पादन अप्रत्याशित है ,

अक्षम है !!

यद्यपि उस पोस्ट में "साइन बिट" संबंधित स्पष्टीकरण दिया गया है, मैं यह समझने में असमर्थ हूं कि यह कैसे संभव है कि हम कुछ सेट करते हैं और फिर यह वैसा ही प्रतिबिंबित नहीं करता है।

क्या कोई और अधिक विस्तृत विवरण दे सकता है?


नोट : दोनों टैग और आवश्यक हैं, क्योंकि उनके मानक बिट-फ़ील्ड का वर्णन करने के लिए थोड़ा भिन्न होते हैं। C विनिर्देश और C ++ विनिर्देशन के उत्तर देखें ।


46
चूंकि बिटफील्ड को घोषित किया गया है क्योंकि intमुझे लगता है कि यह केवल मूल्यों को पकड़ सकता है 0और -1
ओसिरिस

6
बस इसके बारे में सोचें कि इंट -1 कैसे स्टोर करता है। सभी बिट्स 1 पर सेट हैं। इसलिए, यदि आपके पास केवल एक बिट है तो स्पष्ट रूप से -1 होना चाहिए। तो 1 बिट में 1 और -1 समान हैं। चेक को 'if (s.enabled! = 0)' में बदलें और यह काम करता है। क्योंकि 0 यह नहीं हो सकता।
जुरगेन

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

8
क्या आपने इसे बदलने की कोशिश की है struct mystruct { unsigned int enabled:1; };?
चैट्टरऑन

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

जवाबों:


78

बिट-फ़ील्ड अविश्वसनीय रूप से मानक द्वारा परिभाषित खराब हैं। इस कोड को देखते हुए struct mystruct {int enabled:1;};, फिर हम नहीं जानते:

  • यह कितना स्थान घेरता है - यदि पैडिंग बिट्स / बाइट्स हैं और वे स्मृति में स्थित हैं।
  • स्मृति में बिट कहाँ स्थित है। परिभाषित नहीं है और यह भी धीरज पर निर्भर करता है।
  • चाहे एक int:nबिटफील्ड को हस्ताक्षरित या अहस्ताक्षरित माना जाए।

पिछले भाग के बारे में, C17 6.7.2.1/10 का कहना है:

बिट-फ़ील्ड की व्याख्या एक हस्ताक्षरित या अहस्ताक्षरित पूर्णांक प्रकार के रूप में की जाती है जिसमें बिट्स 125 की निर्दिष्ट संख्या होती है )

ऊपर बताए गैर-प्रामाणिक नोट:

125) जैसा कि ऊपर 6.7.2 में निर्दिष्ट किया गया है, यदि वास्तविक प्रकार के निर्दिष्ट विनिर्देशक का उपयोग किया गया है intया टाइप-एफ-नाम के रूप में परिभाषित किया गया है int, तो यह कार्यान्वयन-परिभाषित है कि बिट-फ़ील्ड पर हस्ताक्षर किए गए हैं या अहस्ताक्षरित हैं।

यदि बिटफील्ड को माना जाता है signed intऔर आप थोड़ा सा आकार बनाते हैं 1, तो डेटा के लिए कोई जगह नहीं है, केवल साइन बिट के लिए। यही कारण है कि आपका प्रोग्राम कुछ कंपाइलरों पर अजीब परिणाम दे सकता है।

अच्छा अभ्यास:

  • किसी भी उद्देश्य के लिए कभी भी बिट-फ़ील्ड का उपयोग न करें।
  • intबिट हेरफेर के किसी भी रूप में हस्ताक्षरित प्रकार का उपयोग करने से बचें ।

5
काम पर हमारे पास बिटफिल्ड के आकार और पते पर static_asserts हैं, बस यह सुनिश्चित करने के लिए कि वे गद्देदार नहीं हैं। हम अपने फर्मवेयर में हार्डवेयर रजिस्टरों के लिए बिटफिल्ड का उपयोग करते हैं।
माइकल

4
@ लुंडिन: # डिफाइन-डी मास्क और ऑफसेट के साथ बदसूरत बात यह है कि आपका कोड शिफ्ट्स और बिट-वार और / या ऑपरेटरों से फिसल जाता है। बिटफ़िल्ड के साथ कंपाइलर आपके लिए इसका ख्याल रखता है।
माइकल

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

3
@AndrewHenle Leushenko कह रही है कि सिर्फ सी मानक के परिप्रेक्ष्य से , यह कार्यान्वयन पर निर्भर है कि वह x86-64 एबीआई का पालन करता है या नहीं।
mtraceur

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

58

मैं समझ नहीं पा रहा हूं, यह कैसे संभव है कि हम कुछ सेट करें और फिर वह जैसा है वैसा नहीं दिखा।

क्या आप पूछ रहे हैं कि यह क्यों संकलित करता है बनाम आपको एक त्रुटि देता है?

हां, यह आदर्श रूप से आपको एक त्रुटि देना चाहिए। और यह करता है, यदि आप अपने संकलक की चेतावनियों का उपयोग करते हैं। GCC में, साथ -Werror -Wall -pedantic:

main.cpp: In function 'int main()':
main.cpp:7:15: error: overflow in conversion from 'int' to 'signed char:1' 
changes value from '1' to '-1' [-Werror=overflow]
   s.enabled = 1;
           ^

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

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

(टीएल; डीआर - यदि आप वैध रूप से "आवश्यकता" बिट-फ़ील्ड के लिए पर्याप्त परिष्कृत हैं, तो वे आपकी सेवा करने के लिए पर्याप्त रूप से परिभाषित नहीं हैं।)


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

9
कोई सुसंगत तकनीकी तर्क नहीं है। लेकिन इससे मुझे यह निष्कर्ष निकलता है कि एक राजनीतिक तर्क था: किसी भी मौजूदा कोड या कार्यान्वयन को गलत बनाने से बचने के लिए। लेकिन नतीजा यह है कि बिटफिल्ड के बारे में बहुत कम है जिस पर आप भरोसा कर सकते हैं।
जॉन बोलिंगर

6
@ जॉनहोलिंगर निश्चित रूप से राजनीति में थे, जिससे C90 को बहुत नुकसान हुआ। मैंने एक बार समिति के एक सदस्य के साथ बात की, जिसने बहुत सारे बकवास के स्रोत को समझाया - आईएसओ मानक को कुछ मौजूदा प्रौद्योगिकियों का पक्ष लेने की अनुमति नहीं दी जा सकती है। यही कारण है कि हम 1 के पूरक और हस्ताक्षरित परिमाण के समर्थन, कार्यान्वयन-परिभाषित हस्ताक्षर, charबाइट्स के लिए समर्थन जो 8 बिट्स आदि नहीं हैं, आदि के साथ नैतिक चीजों के साथ फंस गए हैं। उन्हें नैतिक कंप्यूटरों को बाजार का नुकसान देने की अनुमति नहीं थी।
लुंडिन

1
@ लुंडिन उन लोगों से राइटअप और पोस्टमार्टम का संग्रह देखना दिलचस्प होगा, जो मानते थे कि ट्रेडऑफ़ में त्रुटि हुई थी, और क्यों। मुझे आश्चर्य है कि इनमें से कितना अध्ययन हमने "पिछली बार किया था, और इसने किया / नहीं किया" अगले ऐसे मामले को सूचित करने के लिए संस्थागत ज्ञान बन गया है, जैसे कि लोगों के सिर में सिर्फ कहानियाँ।
HostileFork का कहना है कि

1
यह अभी भी बिंदु संख्या के रूप में सूचीबद्ध है। C2x चार्टर में C के मूल सिद्धांतों में से 1: "मौजूदा कोड महत्वपूर्ण है, मौजूदा कार्यान्वयन नहीं हैं।" ... "कोई भी कार्यान्वयन को एक उदाहरण के रूप में आयोजित नहीं किया गया था जिसके द्वारा C को परिभाषित किया गया था: यह माना जाता है कि सभी मौजूदा कार्यान्वयनों को मानक के अनुरूप कुछ बदलना होगा।"
लेउशेंको

23

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

struct mystruct { int enabled:1; };

enable1 बिट बिट-फ़ील्ड के रूप में घोषित करता है। चूंकि यह हस्ताक्षरित है, मान्य मान हैं -1और 0। उस क्षेत्र को 1ओवरफ्लो करने के लिए फ़ील्ड सेट करना जो थोड़ा पीछे जा रहा है -1(यह अपरिभाषित व्यवहार है)

अनिवार्य रूप से एक हस्ताक्षर किए गए बिट-फ़ील्ड के साथ काम करते समय अधिकतम मूल्य 2^(bits - 1) - 1जो 0इस मामले में है।


"यदि यह हस्ताक्षरित है, तो मान्य मान -1 और 0 हैं"। किसने कहा कि यह हस्ताक्षरित है? यह परिभाषित नहीं है बल्कि कार्यान्वयन-परिभाषित व्यवहार है। यदि यह हस्ताक्षरित है, तो मान्य मूल्य हैं -और +। 2 का पूरक कोई फर्क नहीं पड़ता।
लुंडिन

5
@ लुंडिन ए 1 बिट्स की प्रशंसा संख्या केवल दो संभावित मान हैं। यदि बिट सेट है, तो चूंकि यह साइन बिट है, इसलिए यह -1 है। यदि यह सेट नहीं है, तो यह "सकारात्मक" है। 0. मुझे पता है कि यह कार्यान्वयन को परिभाषित किया गया है, मैं सिर्फ सबसे सामान्य आरोपण का उपयोग करके परिणामों की व्याख्या कर रहा हूं
NathanOliver

1
यहाँ कुंजी यह है कि 2 के पूरक या किसी अन्य हस्ताक्षरित रूप में उपलब्ध एकल बिट के साथ कार्य नहीं किया जा सकता है।
लंडिन

1
@ जॉनबोलिंगर मैं समझता हूँ कि। इसलिए मेरे पास डिस्क्लेमर है कि यह क्रियान्वयन परिभाषित है। कम से कम बड़े 3 के लिए वे सभी intइस मामले में हस्ताक्षरित के रूप में व्यवहार करते हैं । यह शर्म की बात है कि बिट-फिल्ड निर्दिष्ट के तहत हैं। यह मूल रूप से यहां है यह सुविधा है, इसका उपयोग करने के तरीके के बारे में अपने संकलक से परामर्श करें।
नेथनऑलिवर

1
@ लुंडिन, हस्ताक्षरित पूर्णांकों के प्रतिनिधित्व के लिए मानक शब्द पूरी तरह से उस मामले को अच्छी तरह से संभाल सकता है जहां शून्य मूल्य बिट्स हैं, कम से कम तीन में से दो में वैकल्पिक विकल्प। यह काम करता है क्योंकि यह एक एल्गोरिथम व्याख्या देने के बजाय, बिट्स पर हस्ताक्षर करने के लिए (नकारात्मक) मान प्रदान करता है ।
जॉन बोलिंगर ने

10

आप इसके बारे में सोच सकते हैं कि 2 के पूरक प्रणाली में, बाएं-बिट बिट साइन बिट है। बाएं-सबसे बिट सेट के साथ कोई भी हस्ताक्षरित पूर्णांक इस प्रकार एक नकारात्मक मूल्य है।

यदि आपके पास 1-बिट हस्ताक्षरित पूर्णांक है, तो इसमें केवल साइन बिट है। इसलिए 1उस एकल बिट को असाइन करना केवल साइन बिट सेट कर सकता है। इसलिए, इसे वापस पढ़ते समय, मान को नकारात्मक के रूप में व्याख्या की जाती है और इसलिए -1 है।

मान 1 बिट हस्ताक्षरित पूर्णांक धारण कर सकता है -2^(n-1)= -2^(1-1)= -2^0= -1 और है2^n-1= 2^1-1=0


8

के अनुसार सी ++ मानक n4713 , एक बहुत ही इसी तरह के कोड का टुकड़ा प्रदान की जाती है। उपयोग किया जाने वाला प्रकार BOOL(कस्टम) है, लेकिन यह किसी भी प्रकार पर लागू हो सकता है।

12.2.4

4 यदि मूल्य सही या गलतboolकिसी भी आकार केबिट-फ़ील्ड(एक बिट बिट-फ़ील्ड सहित)में संग्रहीत किया जाता है, तो मूलboolमूल्य और बिट-फ़ील्ड का मूल्य बराबर की तुलना करेगा। यदि किसी एन्यूमरेटर का मान समान एन्यूमरेशन प्रकार के बिट-फील्ड में संग्रहित किया जाता है और बिट-फील्ड में बिट्स की संख्या उस एन्यूमरेशन टाइप (10.2) के सभी मानों को रखने के लिए पर्याप्त होती है, तो एन्यूमरेटर वैल्यू और बिट-फील्ड का मूल्य बराबर होगा । [ उदाहरण:

enum BOOL { FALSE=0, TRUE=1 };
struct A {
  BOOL b:1;
};
A a;
void f() {
  a.b = TRUE;
  if (a.b == TRUE)    // yields true
    { /* ... */ }
}

- अंतिम उदाहरण]


पहली नज़र में, बोल्ड भाग व्याख्या के लिए खुला दिखाई देता है। हालाँकि, सही आशय स्पष्ट हो जाता है जब enum BOOLसे प्राप्त होता है int

enum BOOL : int { FALSE=0, TRUE=1 }; // ***this line
struct mystruct { BOOL enabled:1; };
int main()
{
  struct mystruct s;
  s.enabled = TRUE;
  if(s.enabled == TRUE)
    printf("Is enabled\n"); // --> we think this to be printed
  else
    printf("Is disabled !!\n");
}

उपरोक्त कोड के साथ यह बिना चेतावनी देता है -Wall -pedantic:

चेतावनी: 'रहस्यमय :: सक्षम' 'एनम BOOL' के सभी मूल्यों को रखने के लिए बहुत छोटा है struct mystruct { BOOL enabled:1; };

आउटपुट है:

अक्षम है !! (उपयोग करते समय enum BOOL : int)

यदि enum BOOL : intइसे सरल बनाया जाता है enum BOOL, तो आउटपुट मानक मान के अनुसार होता है:

सक्षम है (उपयोग करते समय enum BOOL)


इसलिए, यह निष्कर्ष निकाला जा सकता है, जैसा कि कुछ अन्य उत्तरों में है, वह intप्रकार केवल "बिट" को केवल एक बिट बिट-फ़ील्ड में संग्रहीत करने के लिए पर्याप्त बड़ा नहीं है।


0

बिटफिल्ड की आपकी समझ में कुछ भी गलत नहीं है जो मैं देख सकता हूं। जो मैं देख रहा हूं वह यह है कि आपने पहले रहस्य को फिर से परिभाषित किया, जैसा कि संरचनात्मक रहस्य {इंट सक्षम: 1; } और फिर संरचित रहस्यवाद के रूप में ; । आपको कोडित होना चाहिए था:

#include <stdio.h>

struct mystruct { int enabled:1; };
int main()
{
    mystruct s; <-- Get rid of "struct" type declaration
    s.enabled = 1;
    if(s.enabled == 1)
        printf("Is enabled\n"); // --> we think this to be printed
    else
        printf("Is disabled !!\n");
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.