在C/C++中读写Nibble(不使用位域)

4
有没有一种不使用位域的简单方法来读取/写入字节中的半字节?我总是需要读取两个半字节,但需要分别写入每个半字节。
谢谢!
4个回答

6

使用遮罩:

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

您可能希望将此放在宏中。


3
如果您正在更新一个已存在的值,您需要在位或新值之前清除 byte 中的半字节。byte = (byte & ~0xF) | (nibble1 & 0xF),对于高半字节同样如此。 - Mike Seymour
2
不会将变量命名为byte。 - OlimilOops
2
以上代码中有几个错误 - 您需要在字节掩码前面加上 ~ - Paul R
1
如果可能的话,我总是会选择函数而不是宏! - AshleysBrain
nibble1和nibble2定义在哪里? - AntonioCS
显示剩余2条评论

2

您能够处理的最小单位是一个字节。如果您想要管理位,应该使用按位运算符


0
这是一个考虑到C++11的现代答案:
// 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);
}

主要优点:

  • 没有令人讨厌的union技巧
  • 没有丑陋的宏
  • 没有样板文件
  • 使用标准化的固定宽度类型
  • contexpr函数
    • (即可用于编译时计算和模板参数。)
  • 非常简单明了

(在任何人提问之前,>> 0<< 0主要是为了视觉平衡,以展示即使在实际上不需要移位的特殊情况下,相同的概念也在使用中。如果您的编译器不能将其优化掉,请向您的编译器提供商投诉,而不是我。)


然而,如果你的 nibbles 实际上代表了重要的内容,例如位域, 那么你也许想要创建一个 class/struct

例如,如果你正在编写一个需要带有索引 16 色值的帧缓冲器的设备,每两个像素值打包成一个字节,你可能会想要创建这样的东西:

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

请注意,这本质上只是对上述函数的微不足道的包装。
在这种情况下,它提供了:
  • 类型安全 - 不会意外混淆普通的uint8_t和特定指定的PixelPair。(另请参见:Bjarne Stroustrup's 2012 Keynote,他在其中讨论了“类型丰富的编程”)。
  • 提高可读性 - pixelPair.getLeftPixel()告诉您代码正在处理什么:一对像素的左侧像素。
  • 清晰的语义 - 代码告诉您它正在处理什么,而不是如何处理它。 pixelPair.getLeftPixel()告诉您该函数正在检索左侧像素,而没有指定如何检索,而getHighNibble(pixelByte)只告诉您如何检索高位半字节,它并没有告诉您该半字节代表什么 - 也许高位半字节实际上代表右侧像素?
你还可以进一步创建一个Pixel类,如果你想要更多的类型安全性,它可以具有处理特定像素格式相关功能。这种类型的代码让你思考你正在处理什么样的数据以及数据之间的关系,而不仅仅是将数据视为位和字节的数量。

-2
你可以为了方便自己创建一个伪联合:
union ByteNibbles
{
    ByteNibbles(BYTE hiNibble, BYTE loNibble)
    {
        data = loNibble;
        data |= hiNibble << 4;
    }

    BYTE data;
};

使用方法如下:

ByteNibbles byteNibbles(0xA, 0xB);

BYTE data = byteNibbles.data;

这是一个伪联合体以加强一个概念。我宁愿使用它而不是一些有缺陷的宏。 - DanDan
2
我更喜欢使用内联函数而不是宏和伪联合。;-) - Peter G.
如果您要使用此方法,请不要将其设置为“union”,而应将其设置为“struct”,并且不要构造一个对象,然后立即丢弃它,而是保留该对象。如果您发现从持有“ByteNibbles”中获得任何好处,则首先使用简单的makeByte(high,low)函数即可。 - Pharap

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接