【发布时间】:2010-10-26 04:50:40
【问题描述】:
我正在编写网络标头,许多协议使用 4 位字段。有没有一种方便的类型可以用来表示这些信息?
我发现的最小类型是 BYTE。然后我必须使用大量的二进制操作来仅引用该变量中的几个位。
【问题讨论】:
我正在编写网络标头,许多协议使用 4 位字段。有没有一种方便的类型可以用来表示这些信息?
我发现的最小类型是 BYTE。然后我必须使用大量的二进制操作来仅引用该变量中的几个位。
【问题讨论】:
由于内存是字节寻址的,你不能寻址任何小于单个字节的单元。但是,您可以构建要通过网络发送的struct,并像这样使用bit fields:
struct A {
unsigned int nibble1 : 4;
unsigned int nibble2 : 4;
};
【讨论】:
unsigned char。在 gcc 中,您可以通过在右大括号之后(分号之前)添加 attribute__((__packed)) 来做到这一点。在 MSVC 中,您将结构声明包含在 #pragma pack (push,1) 和 #pragma pack (pop) 指令中(为了兼容性,gcc 也支持这一点)
unsigned char 的坏处是您仍然无法确定特定编译器没有添加填充以将其与单词或双字边界对齐(虽然本示例是这种情况,但可能不适用于更复杂的情况) .
扩展 Mehrdads 的答案,同时使用带字节的联合以避免一些看起来很邪恶的类型转换:
union Nibbler {
struct {
unsigned int first:4;
unsigned int second:4;
} nibbles;
unsigned char byte_value;
}
【讨论】:
似乎每个人都喜欢为此使用structs 中的位域。就个人而言,我将我所有的数据包代码都包装在对象中,这样你就看不到胆量了。我发现在协议代码中使用位域的问题是它鼓励使用结构作为内存上的覆盖。您可以安全地执行此操作,但您必须非常小心,以确保正确处理字节序和打包问题。除非你真的有充分的理由(例如,你正在编写从内存映射 IO 区域接收以太网数据包的代码),否则使用覆盖在内存上的位域会产生极其脆弱的代码恕我直言。
我发现编写一个Packet 类要容易得多,它实现了各种位宽的提取、插入和覆盖例程。然后,根据从偏移量中提取特定宽度的值到本机整数等方面来实现数据包处理代码。隐藏抽象背后的所有字节序和打包问题,直到分析证明开销太大而无法承受。
这是我希望我多年前学到的经验之一……您可能认为代码的可移植性不是问题,字节序也不是问题。相信我,当你的编译器改变它的填充算法或者你切换到不同的编译器时,这会给你带来多少麻烦,这会让你相信覆盖对于网络数据包处理代码来说是一个非常糟糕的想法。 p>
【讨论】:
在结构中使用字段:
struct Header
{
unsigned int lowestNibble : 4;
unsigned int anotherNibble : 4;
unsigned int : 18; # Unnamed padding.
bool aBool : 1;
bool anotherBool : 1;
unsigned int highestNibble : 4;
};
: 4 表示条目应占用 4 位。您可以使用任意数量的位。你可以使用任何你喜欢的内置类型。
通常,您最终会将指向数据的指针转换为 Header *,然后执行以下操作:
pHeader->lowestNibble = 5;
【讨论】:
不,没有方便的半字节类型。但是,使用宏或模板函数很容易制作它们。如果/当您需要处理字节序时,这尤其适用。
食客
【讨论】: