开发者

Reading/Writing Nibbles (without bit fields) in C/C++

开发者 https://www.devze.com 2023-01-04 19:44 出处:网络
Is there an easy way to read/write a nibble in a byte without using bit field开发者_如何学运维s?

Is there an easy way to read/write a nibble in a byte without using bit field开发者_如何学运维s? I'll always need to read both nibbles, but will need to write each nibble individually.

Thanks!


Use masks :

char byte;
byte = (byte & 0xF0) | (nibble1 & 0xF); // write low quartet
byte = (byte & 0x0F) | ((nibble2 & 0xF) << 4); // write high quartet

You may want to put this inside macros.


The smallest unit you can work with is a single byte. If you want to manage the bits you should use bitwise operators.


Here's a modern answer that takes C++11 into account:

// Fixed-width integer types
#include <cstdint>

// Constexpr construction
constexpr uint8_t makeByte(uint8_t highNibble, uint8_t lowNibble)
{
    return (((highNibble & 0xF) << 4) | ((lowNibble & 0xF) << 0));
}

// Constexpr high nibble extraction
constexpr uint8_t getHighNibble(uint8_t byte)
{
    return ((byte >> 4) & 0xF);
}

// Constexpr low nibble extraction
constexpr uint8_t getLowNibble(uint8_t byte)
{
    return ((byte >> 0) & 0xF);
}

Big benefits:

  • No nasty union trickery
  • No ugly macros
  • No boilerplate
  • Using standardised fixed-width types
  • contexpr functions
    • (I.e. can be used in compile-time calculations and template paramters.)
  • Just plain simple

(Before anyone asks, the >> 0 and << 0 are for primarily for visual balance, to demonstrate that the same concept is in use even in the exceptional case where no shift is actually needed. If your compiler doesn't optimise those away, complain to your compiler provider, not me.)


However, if your nibbles actually represent something important, e.g. a bitfield, then you might want to create a class/struct.

For example if you were programming a device that required a frame buffer with index 16-colour values, with 2 pixel values packed per byte, you might want to create something like this:

struct PixelPair
{
private:
    uint8_t value;

public:
    contexpr explicit PixelPair(uint8_t rawValue) :
        value { rawValue }
    {
    }

    constexpr PixelPair(uint8_t leftPixel, uint8_t rightPixel) :
        value { makeByte(leftPixel, rightPixel) }
    {
    }
    
    constexpr uint8_t getLeftPixel() const
    {
        return getHighNibble(this->value);
    }
    
    constexpr uint8_t getRightPixel() const
    {
        return getLowNibble(this->value);
    }
    
    constexpr uint8_t getRawValue() const
    {
        return this->value;
    }
};

Note that this is essentially just a vanishingly thin wrapper around the above functions.

In this case it provides:

  • Type safety - No accidentally mixing up a plain old uint8_t and a specifically designated PixelPair. (See also: Bjarne Stroustrup's 2012 Keynote, where he discusses "Type-rich Programming".)
  • Improved readability - pixelPair.getLeftPixel() tells you exactly what the code is dealing with: the left-hand pixel of a pair of pixels.
  • Clear semantics - The code tells you what it is dealing with, not how it is dealing with it. pixelPair.getLeftPixel() tells you that the function is retrieving the left-hand pixel without specifying how, whereas getHighNibble(pixelByte) only tells you the how, i.e. that the high nibble of a pixel byte is being retrieved, it doesn't tell you what that nibble represents - perhaps the high nibble actually represents the right-hand pixel?

You could take this further and create a Pixel class too if you wanted even more type safety, and it could have relevant functions for dealing with the specific pixel format. This sort of code gets you thinking about what kind of data you are dealing with and the relationships between the data, rather than just thinking about the data as quantities of bits and bytes.


You could create yourself a pseudo union for convenience:

union ByteNibbles
{
    ByteNibbles(BYTE hiNibble, BYTE loNibble)
    {
        data = loNibble;
        data |= hiNibble << 4;
    }

    BYTE data;
};

Use it like this:

ByteNibbles byteNibbles(0xA, 0xB);

BYTE data = byteNibbles.data;
0

精彩评论

暂无评论...
验证码 换一张
取 消