Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access bits in a char in C

Tags:

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?

like image 685
kkh Avatar asked Dec 21 '11 02:12

kkh


People also ask

How many bits does a char have in C?

The smallest group of bits the language allows use to work with is the unsigned char , which is a group of 8 bits.

How to read a certain bit in C?

To read a bit at a specific position, you must mask out all other bits in the value. The operator that assists in that process is the bitwise & (and). After you mask out all the other bits, the value that remains is either zero or some other value.

How do I access a specific bit?

For accessing a specific bit, you can use Shift Operators . If it is always a 1 that you are going to reset, then you could use an & operation. But, if it can also take 0 value, then & operation will fail as 0 & 1 = 0 . You could use | (OR) during that time.

How to manipulate a single bit in C?

Bitwise operator works on bits and perform bit by bit operation. Binary AND Operator copies a bit to the result if it exists in both operands. Binary OR Operator copies a bit if it exists in eather operand. Binary XOR Operator copies the bit if it is set in one operand but not both.


3 Answers

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

char c = 0x37;
char mask = 0x03;
char lastTwo = c & mask;
like image 143
Sergey Kalinichenko Avatar answered Oct 18 '22 18:10

Sergey Kalinichenko


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;
}
like image 38
shinkou Avatar answered Oct 18 '22 18:10

shinkou


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.

like image 6
steveha Avatar answered Oct 18 '22 20:10

steveha