Access bits in a char in C

前端 未结 5 1545
别跟我提以往
别跟我提以往 2020-12-09 18:10

I have a hex number 0x37 and its binary representation is 0011 0111. How do I access the first 2 bits of the binary representation which is \"11\"? How do I use bit shifting

相关标签:
5条回答
  • 2020-12-09 18:47

    This is the easiest function to use, I don't want others to struggle for a long time before getting something similar to this -

    char get_bits(char a, char no_of_bits)
    {
        return a & ((no_of_bits << 1) - 1);
    }
    char a = 0x37;
    char b = get_bits(a, 2);
    

    Hope it helps someone in future

    0 讨论(0)
  • 2020-12-09 18:51

    If you & your number with 0x03, you will get the last two bits.

    char c = 0x37;
    char mask = 0x03;
    char lastTwo = c & mask;
    
    0 讨论(0)
  • 2020-12-09 18:57

    You could also use bit-fields to do this. The bad part about bit-fields is that exactly how they work is somewhat compiler-dependent, but if you don't need to port your code to many architectures, maybe it's fine.

    Here is an example, written on an Ubuntu Linux computer and tested with GCC.

    #include <assert.h>
    #include <stdio.h>
    
    #pragma pack(1)
    typedef struct
    {
        unsigned int low2: 2;  // 2 bits of the byte
        unsigned int high6: 6;  // 6 more bits of the byte
    } MYBYTE;
    
    typedef union
    {
        MYBYTE mybyte;
        unsigned char b;
    } MYUNION;
    
    main()
    {
        MYUNION m;
    
        assert(sizeof(m) == 1);
        m.b = 0x03;
        assert(m.mybyte.low2 == 0x03);
        assert(m.mybyte.high6 == 0x00);
    
        printf("low2 of 0x03 is: %u\n", m.mybyte.low2);
        printf("high6 of 0x03 is: %u\n", m.mybyte.high6);
    
        m.b = 0xff;
    
        printf("low2 of 0x03 is: %u\n", m.mybyte.low2);
        printf("high6 of 0x03 is: %u\n", m.mybyte.high6);
        assert(m.mybyte.low2 == 0x03);
        assert(m.mybyte.high6 == 0x3f);
    
        m.mybyte.high6 = 0x1c;
        m.mybyte.low2 = 0x01;
        assert(m.b == 0x71);
        printf("m.b is: 0x%02x\n", m.b);
    
        return 0;
    }
    

    The union is there so we can access it as a full byte, or access it by bit fields. #pragma pack(1) is there to make sure the bit fields pack down to a byte, with no extra "padding" bits in there. (As I said before, you are relying on implementation details when you use bit fields.)

    But look how simple and clean it is to access the bits you want. You can write in a whole byte and read out the bits you want, or write in the bits you want and read out the whole byte.

    If you are going to use code like this, it's always a good idea to have some asserts that make sure it is working.

    If you are not going to use bit fields, I suggest you define a function that does your shifting and masking for you, to make sure you don't mess up. Maybe something like this:

    #include <limits.h>
    
    
    static unsigned int _bit_masks[] =
    {
        0x00000000, 0x00000001, 0x00000003, 0x00000007,
        0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f,
        0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff,
        0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff,
        0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff,
        0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff,
        0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff,
        0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff,
    };
    
    
    #define MIN(a, b) \
        ((a) < (b) ? (a) : (b))
    
    unsigned int
    bits(unsigned int x, unsigned int i_bit, unsigned int c_bits)
    {
        assert(UINT_MAX >= 4294967295U); // unsigned int must be at least 32-bit
        assert(i_bit <= 31);
        if (i_bit > 31)
            return 0;
        c_bits = MIN(c_bits, 32 - i_bit);
    
        // shift-and-mask to grab the requested bits, and return those bits
        return (x >> i_bit) & _bit_masks[c_bits];
    }
    

    You pass in a value, then which bit position you want bits from, and how many bits you want. So to grab 6 bits starting from the bit position 2, with a test value of 0x71 you could call:

    x = bits(0x71, 2, 6);  // x is set to 0x1c
    

    If you don't like the lookup table, and you want the tiniest code to do this, you can use:

    unsigned int
    bits(unsigned int x, unsigned int i_bit, unsigned int c_bits)
    {
        const unsigned int mask_bits = 0xffffffff;
    
        assert(UINT_MAX >= 4294967295U); // unsigned int must be at least 32-bit
        assert(i_bit <= 31);
        if (i_bit > 31)
            return 0;
        c_bits = MIN(c_bits, 32 - i_bit);
    
        // shift-and-mask to grab the requested bits, and return those bits
        return (x >> i_bit) & (mask_bits >> (32 - c_bits));
    }
    

    You need to make sure that the mask bits are declared unsigned because if they are signed, the right-shift operation will sign-extend.

    If you declare this last version of the function as inline, put it into header files, and call it with constant values for i_bit and c_bits, it will compile down to the minimal code to solve the problem. (For example, if i_bit is 0, the compiler knows that >> 0 doesn't do anything and will just not generate that code. And if the compiler knows c_bits as a constant, it can do all the work of shifting mask_bits at compile time.) But you will need to make sure you are using a version of assert() that compiles away to nothing in your release build, or else use your own ASSERT() macro and make your macro compile away to nothing.

    0 讨论(0)
  • 2020-12-09 19:05

    Your best bet is to use bit masking, as you had mentioned. Something like this should do the trick:

    x = 0x37;
    y = x&0x30; //Mask out the first two bits of the higher nibble
    y = y>>4;
    
    0 讨论(0)
  • 2020-12-09 19:10

    Here is a sample to access it bit by bit:

    #include <stdio.h>
    int main()
    {
        char byte = 0x37;
        int i;
    
        for(i = 7; 0 <= i; i --)
            printf("%d\n", (byte >> i) & 0x01);
    
        return 0;
    }
    
    0 讨论(0)
提交回复
热议问题