c++ सी/सी++ में मनमाने ढंग से बिट्स को कैसे पढ़ा/लिखना है




memory bit (7)

इस सवाल से पूछने के कुछ 2+ साल बाद मैं इसे समझाना चाहता हूं कि जिस तरह से मैं यह चाहता हूं कि जब मैं अभी भी एक पूर्ण नौसिखिया था तब यह समझाया जाएगा और प्रक्रिया को समझना चाहते लोगों के लिए सबसे फायदेमंद होगा।

सबसे पहले, "11111111" उदाहरण मान भूल जाएं, जो वास्तव में प्रक्रिया के दृश्य स्पष्टीकरण के लिए उपयुक्त नहीं है। तो प्रारंभिक मान 10111011 (187 दशमलव) होना चाहिए जो प्रक्रिया के थोड़ा और अधिक स्पष्ट होगा।

1 - दूसरे बिट से शुरू होने वाले 3 बिट मान को कैसे पढ़ा जाए:

    ___  <- those 3 bits
10111011 

मान 101 है, या 5 दशमलव में, इसे प्राप्त करने के 2 संभावित तरीके हैं:

  • मास्क और शिफ्ट

इस दृष्टिकोण में, आवश्यक बिट्स को पहले 00001110 (14 दशमलव) के साथ मुखौटा किया जाता है जिसके बाद इसे स्थानांतरित किया जाता है:

    ___
10111011 AND
00001110 =
00001010 >> 1 =
     ___
00000101

इसके लिए अभिव्यक्ति होगी: (value & 14) >> 1

  • शिफ्ट और मुखौटा

यह दृष्टिकोण समान है, लेकिन संचालन का क्रम उलट दिया गया है, जिसका अर्थ है कि मूल मान स्थानांतरित हो गया है और उसके बाद 00000111 (7) के साथ 00000111 केवल अंतिम 3 बिट्स छोड़ने के लिए है:

    ___
10111011 >> 1
     ___
01011101 AND
00000111
00000101

इसके लिए अभिव्यक्ति होगी: (value >> 1) & 7

दोनों दृष्टिकोणों में समान जटिलता शामिल है, और इसलिए प्रदर्शन में भिन्न नहीं होगा।

2 - दूसरी बिट से शुरू होने वाला 3 बिट मान कैसे लिखें:

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

इसका मतलब यह है कि नए मूल्य को बाइट में सफलतापूर्वक "विभाजित" करने के लिए, लक्ष्य बिट्स को शून्य पर सेट किया जाना चाहिए, जिसके बाद स्थानांतरित मूल्य स्थान पर "विभाजित" होता है, जो पहला चरण है:

    ___ 
10111011 AND
11110001 (241) =
10110001 (masked original value)

दूसरा कदम उस मूल्य को स्थानांतरित करना है जिसे हम 3 बिट्स में लिखना चाहते हैं, कहें कि हम इसे 101 (5) से 110 (6) में बदलना चाहते हैं।

     ___
00000110 << 1 =
    ___
00001100 (shifted "splice" value)

तीसरा और अंतिम चरण मुखौटा मूल मूल्य को "splice" मान के साथ विभाजित करना है:

10110001 OR
00001100 =
    ___
10111101

पूरी प्रक्रिया के लिए अभिव्यक्ति होगी: (value & 241) | (6 << 1) (value & 241) | (6 << 1)

बोनस - पढ़ने और लिखने के मास्क कैसे उत्पन्न करें:

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

  • "मुखौटा और शिफ्ट" के लिए मुखौटा पढ़ें: ((1 << fieldLength) - 1) << (fieldIndex - 1) , यह मानते हुए कि पहली बिट पर सूचकांक 1 (शून्य नहीं) है
  • "शिफ्ट और मास्क" के लिए मास्क पढ़ें: (1 << fieldLength) - 1 (इंडेक्स यहां कोई भूमिका निभाता नहीं है क्योंकि इसे हमेशा पहले बिट में स्थानांतरित किया जाता है
  • मास्क लिखें: ~ मास्क और शिफ्ट "मास्क अभिव्यक्ति को ~ ऑपरेटर के साथ उलटा करें

यह कैसे काम करता है (उपरोक्त उदाहरणों से दूसरे बिट पर शुरू होने वाले 3 बिट फ़ील्ड के साथ)?

00000001 << 3
00001000  - 1
00000111 << 1
00001110  ~ (read mask)
11110001    (write mask)

वही उदाहरण बड़े पूर्णांक और मनमाने ढंग से बिट चौड़ाई और फ़ील्ड की स्थिति पर लागू होते हैं, शिफ्ट और मुखौटा मान तदनुसार भिन्न होते हैं।

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

यहां तक ​​कि आसान:

मैक्रोज़ के इस सेट का उपयोग करना (लेकिन केवल सी ++ में क्योंकि यह सदस्य कार्यों की पीढ़ी पर निर्भर करता है):

#define GETMASK(index, size) (((1 << (size)) - 1) << (index))
#define READFROM(data, index, size) (((data) & GETMASK((index), (size))) >> (index))
#define WRITETO(data, index, size, value) ((data) = ((data) & (~GETMASK((index), (size)))) | ((value) << (index)))
#define FIELD(data, name, index, size) \
  inline decltype(data) name() { return READFROM(data, index, size); } \
  inline void set_##name(decltype(data) value) { WRITETO(data, index, size, value); }

आप कुछ आसान के रूप में जा सकते हैं:

struct A {
  uint bitData;
  FIELD(bitData, one, 0, 1)
  FIELD(bitData, two, 1, 2)
};

और बिट फ़ील्ड्स को उन गुणों के रूप में लागू किया गया है जिन्हें आप आसानी से एक्सेस कर सकते हैं:

A a;
a.set_two(3);
cout << a.two();

जीसीसी के decltype प्री-सी ++ 11 के साथ decltype बदलें।

मान लें कि मेरे पास 11111111 के द्विआधारी मूल्य के साथ एक बाइट बी है

उदाहरण के लिए मैं दूसरे बिट पर शुरू होने वाले 3 बिट पूर्णांक मान को कैसे पढ़ूं या पांचवें बिट से शुरू होने वाला चार बिट पूर्णांक मान लिखूं?


आपको एक शिफ्ट और मास्क (एंड) ऑपरेशन करना है। बी को किसी बाइट और पी को उस बिट के इंडेक्स (> = 0) होने दें जिससे आप n बिट्स (> = 1) लेना चाहते हैं।

सबसे पहले आपको पी बी द्वारा सही बी को स्थानांतरित करना होगा:

x = b >> p;

दूसरा आपको परिणाम के साथ मुखौटा करना होगा:

mask = (1 << n) - 1;
y = x & mask;

आप सब कुछ मैक्रो में डाल सकते हैं:

#define TAKE_N_BITS_FROM(b, p, n) ((b) >> (p)) & ((1 << (n)) - 1)

बाइट्स पढ़ने के लिए std :: बिटसेट का उपयोग करें

const int bits_in_byte = 8;

char myChar = 's';
cout << bitset<sizeof(myChar) * bits_in_byte>(myChar);

लिखने के लिए आपको बिट-वार ऑपरेटरों जैसे & ^ | का उपयोग करने की आवश्यकता है और << >>। यह सुनिश्चित करना सुनिश्चित करें कि वे क्या करते हैं।

उदाहरण के लिए 00100100 रखने के लिए आपको पहले बिट को 1 सेट करने की आवश्यकता है, और इसे << >> ऑपरेटरों के साथ 5 बार स्थानांतरित करें। अगर आप लिखना जारी रखना चाहते हैं तो आप पहले बिट को सेट करना जारी रखें और इसे स्थानांतरित करें। यह एक पुराने टाइपराइटर की तरह है: आप लिखते हैं, और कागज को स्थानांतरित करते हैं।

00100100 के लिए: पहली बिट को 1 पर सेट करें, 5 बार स्थानांतरित करें, पहले बिट को 1 पर सेट करें, और 2 बार स्थानांतरित करें:

const int bits_in_byte = 8;

char myChar = 0;
myChar = myChar | (0x1 << 5 | 0x1 << 2);
cout << bitset<sizeof(myChar) * bits_in_byte>(myChar);

बस इस और feelfree का उपयोग करें:

#define BitVal(data,y) ( (data>>y) & 1)      /** Return Data.Y value   **/
#define SetBit(data,y)    data |= (1 << y)    /** Set Data.Y   to 1    **/
#define ClearBit(data,y)  data &= ~(1 << y)   /** Clear Data.Y to 0    **/
#define TogleBit(data,y)     (data ^=BitVal(y))     /** Togle Data.Y  value  **/
#define Togle(data)   (data =~data )         /** Togle Data value     **/

उदाहरण के लिए:

uint8_t number = 0x05; //0b00000101
uint8_t bit_2 = BitVal(number,2); // bit_2 = 1
uint8_t bit_1 = BitVal(number,1); // bit_1 = 0

SetBit(number,1); // number =  0x07 => 0b00000111
ClearBit(number,2); // number =0x03 => 0b0000011

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

struct bitfield{
    unsigned int bit : 1
}
struct bitfield *bitstream;

फिर बाद में इसे लोड करने पर (int int को int के साथ बदलना या जो भी डेटा आप लोड कर रहे हैं):

long int i;
int j, k;
unsigned char c, d;

bitstream=malloc(sizeof(struct bitfield)*charstreamlength*sizeof(char));
for (i=0; i<charstreamlength; i++){
    c=charstream[i];
    for(j=0; j < sizeof(char)*8; j++){
        d=c;
        d=d>>(sizeof(char)*8-j-1);
        d=d<<(sizeof(char)*8-1);
        k=d;
        if(k==0){
            bitstream[sizeof(char)*8*i + j].bit=0;
        }else{
            bitstream[sizeof(char)*8*i + j].bit=1;
        }
    }
}

फिर तत्वों का उपयोग करें:

bitstream[bitpointer].bit=...

या

...=bitstream[bitpointer].bit

यह सब मानते हैं कि आई 86/64 पर काम नहीं कर रहे हैं, हाथ नहीं, क्योंकि हाथ बड़ा या छोटा एंडियन हो सकता है।


"उदाहरण के लिए मैं दूसरे बिट पर शुरू होने वाले 3 बिट पूर्णांक मान को कैसे पढ़ूं?"

int number = // whatever;
uint8_t val; // uint8_t is the smallest data type capable of holding 3 bits
val = (number & (1 << 2 | 1 << 3 | 1 << 4)) >> 2;

(मुझे लगता है कि "दूसरा बिट" बिट # 2 है, यानी तीसरा बिट वास्तव में।)


int x = 0xFF;   //your number - 11111111

उदाहरण के लिए मैं दूसरे बिट पर शुरू होने वाले 3 बिट पूर्णांक मान को कैसे पढ़ूं

int y = x & ( 0x7 << 2 ) // 0x7 is 111
                         // and you shift it 2 to the left






read-write