简体   繁体   中英

Access bits in a char in C

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 or masking to achieve this? I can access bit by bit but not two bits at one go?

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

char c = 0x37;
char mask = 0x03;
char lastTwo = c & mask;

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;
}

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.

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;

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM