简体   繁体   中英

Getting and setting bits in a byte in C/C++

I've created a function that enables you to get a range of bits in a byte, counting bit 0 for the least significant bit (LSb) on the right and 7 for the most significant bit (MSb) on the left for code in C or C++. However, it's not so straightforward to set bits. This can be generalized for short, int, long etc., but for now I'm sticking with bytes.

Given the following:

#include <stdio.h>
#typedef unsigned char BYTE;

BYTE getByteBits(BYTE n, BYTE b) {
    return (n < 8) ? b & ((0x01 << (n + 1)) - 1) : 0;
}

where the bits we want to extract are between bits 0 and n, and b is the full byte. If n is 0 only 0 or 1 is returned for this LSb, if n is 1, values between 0 and 3 can be returned, etc., up to n is 7 for the full byte. Of course in the latter case the code is redundant.

This can be called from main() by using something like:

BYTE num = getByteBits(4, myByte);

which will return the numerical value from the 4 lowest bits. However, I've discovered that this can be generalized to:

BYTE num = getByteBits(n - m, myByte >> m);

which will extract the value returned by the bits from m to n, such that 0 <= m <= n <= 7. This is done by shifting myByte by m bits to the right, which is equivalent to shifting the mask to the left.

However, so far I've been unable to do something similar to set bits using a function with three arguments. The best I can do is to create the function:

BYTE setByteBits(BYTE n, BYTE m, BYTE b, BYTE c) {
    BYTE mask = ((0x01 << (n + 1)) - 1) << m;
    return (b & ~mask) | (c & mask);
}

where n and m are the same as before, b is the value of the original byte, and c is the value of some of the bits we want to change. The update byte is returned. This would be called as:

BYTE num = setByteBits(n - m, m, MyByte, c << m);

but 4 rather than 3 arguments are needed, and rather than shifting myByte by m bits to the right, the mask is shifted to the left together with its complement in the function, as given be the 2nd argument m. Although as far as I can see this works correctly, so far all attempts to do something similar as getByteBits() with 3 arguments have failed.

Does anybody have any idea about this? Also I would appreciate if any bugs can be found in the code. Incidentally, for setByteBits() I made use of the link:

How do you set only certain bits of a byte in C without affecting the rest?

Many thanks.

so far I've been unable to do something similar to set bits using a function with three arguments

I do not understand your code, because you use so many m n c b one letter variables. Maybe try to be more descriptive? You don't have to write short code, there is no performance gain in that.

When n is the stopping bit position inside the byte, 1 << (n + 1) will give you too big mask. You have to shift 1 of the length of the range of bits, and then that mask shift to the left by the start position. I mixed n with m so many times in your code, I do not know which is which.

The following works, at least for tests I tried:

#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <limits.h>

typedef unsigned char byte;
#define BYTE_BITS  CHAR_BIT
typedef uint_fast8_t bitpos;

/**
 * Set bits in the byte `thebyte`
 * between the range of bit `rangestart` inclusive to `rangestop` exclusive
 * counting from 0 from LSB
 * the the range of these bits inside `masktoset`.
 */
byte setByteBits(bitpos rangestart,
        bitpos rangestop,
        byte thebyte,
        byte masktoset) {
    assert(rangestop <= BYTE_BITS);
    assert(rangestart < rangestop);
    const bitpos rangelen = rangestop - rangestart;
    const byte rangemask = (1u << rangelen) - 1u;
    const byte mask = rangemask << rangestart;
    return (thebyte & ~mask) | (masktoset & mask);
}

int main() {
    const int tests[][5] = {
        { 4,6,0,0xff,0b00110000 },
        { 4,6,0,0b01010101,0b00010000 },
        { 4,6,0,0b01100101,0b00100000 },
        { 4,6,0xff,0b01000101,0b11001111 },
        { 0,8,0,0xff,0xff },
        { 0,8,0,0xab,0xab },
        { 0,4,0xfa,0xab,0xfb },
        { 0,4,0xab,0xcd,0xad },
        { 4,8,0xab,0xcd,0xcb },
        { 4,8,0xef,0xab,0xaf },
    };
    for (size_t i = 0; i <sizeof(tests)/sizeof(*tests); ++i) {
        const int *const t = tests[i];
        const byte r = setByteBits(t[0],t[1],t[2],t[3]);
        fprintf(stderr, "%d (%#02x,%#02x,%#02x,%#02x)->%#02x ?= %#02x\n",
            i,t[0],t[1],t[2],t[3],r,t[4]);
        assert(r == t[4]);
    }
}

Many thanks - I created a new project in Visual Studio 2022, then copied and pasted your code into an empty source file without modifications.

On compiling I get a couple of warning messages as follows:

Rebuild started...

1>------ Rebuild All started: Project: test-binary2, Configuration: Debug x64 ------

1>main.c

1>C:\Users\jeffi\source\repos\test-binary2\main.c(44,25): warning C4477: 'fprintf' : format string '%d' requires an argument of type 'int', but variadic argument 1 has type 'size_t'

1>C:\Users\jeffi\source\repos\test-binary2\main.c(44,25): message : consider using '%zd' in the format string

1>test-binary2.vcxproj -> C:\Users\jeffi\source\repos\test-binary2\x64\Debug\test-binary2.exe

1>Done building project "test-binary2.vcxproj".

========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========

Then on running I get the following output:

0 (0x4,0x6,00,0xff)->0x30 ?= 0x30

1 (0x4,0x6,00,0x55)->0x10 ?= 0x10

2 (0x4,0x6,00,0x65)->0x20 ?= 0x20

3 (0x4,0x6,0xff,0x45)->0xcf ?= 0xcf

4 (00,0x8,00,0xff)->0xff ?= 0xff

5 (00,0x8,00,0xab)->0xab ?= 0xab

6 (00,0x4,0xfa,0xab)->0xfb ?= 0xfb

7 (00,0x4,0xab,0xcd)->0xad ?= 0xad

8 (0x4,0x8,0xab,0xcd)->0xcb ?= 0xcb

9 (0x4,0x8,0xef,0xab)->0xaf ?= 0xaf

When copying and pasting the output to the page for my reply, the lines are concatenated, so I added a newline after each line of output from both outputs. Other than the extra spaces, is this what you get?

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