我正在开发一个发送TCP报头的程序(模拟3握手(。我有一个变量,这个变量包含数据偏移(3位(、保留(4位(和9个标志(9位(。我正在使用逐位操作来设置位。问题是我如何打印每一个比特?
- 假设我存储从2到4(从左到右(开始的数据偏移量:例如0111000000000000
如何打印3位?变量:
u_int16_t reserved_ofs_flags;
我发现这个问题很相似,但答案只适用于最不重要的问题:我如何打印一位?
要从位置P
开始的unsigned
值中提取N
位,从0
开始计算最低有效位,可以使用以下表达式:
unsigned x = (value >> P) & ((1U << (N - 1) << 1) - 1);
注:
- 从左到右对位进行编号是一种令人困惑的惯例。在软件中,首选的编号方法是从最低有效位(数字0(到最高有效位(示例中的数字15(
- 如果
N
是编译时常数,则在编译时计算表达式((1U << (N - 1) << 1) - 1)
- 该表达式假定
N
至少是1
并且最多是unsigned
类型中的位数 - 如果
N
是unsigned
类型中的位数,则更简单的表达式((1U << N) - 1)
具有未定义的行为 - 对于您的示例,
P
是12
,N
是3
,因此您可以写:unsigned x = (value >> 12) & 7;
您可以使用逐位操作来获得所需的位。
例如:
unsigned int a = 22; // 10110 in binary
printf("%dn", a & 1); // get the bit in the 1's place (0)
printf("%dn", (a >> 1) & 1); // get the bit in the 2's place (1)
printf("%dn", (a >> 2) & 1); // get the bit in the 4's place (1)
printf("%dn", (a >> 3) & 1); // get the bit in the 8's place (0)
printf("%dn", (a >> 4) & 1); // get the bit in the 16's place (1)
// 3 in decimal is 11 in binary
printf("%dn", a & 3); // get the bits in the 1's and the 2's places
// (2 in decimal, 10 in binary)
printf("%dn", (a >> 1) & 3); // get the bits in the 2's and the 4's places
// (3 in decimal, 11 in binary)
如果我理解您的问题,并且您希望能够提取从位置P
(从0
到sizeof(type) * CHAR_BIT - 1
(开始的N
比特数(从1
到sizeof(type) * CHAR_BIT
(的值,那么您可以使用提取该比特子集
/** extract N bits from value starting at position P,
* counting from 0 for the least significant bit
*/
unsigned nbitsatp (unsigned value, unsigned N, unsigned P)
{
/* mask is N 1's bits */
unsigned mask = ~0u >> ((sizeof mask * CHAR_BIT) - N);
return (value >> (P - N + 1)) & mask;
}
(注意:对于4字节unsigned
,N
的范围从1
到32
,而P
是从0
到31
的零基(
上面,mask
从所有比特1
(~0u
(开始,然后偏移掉比特总数减去N
(留下N
的1个比特(。然后,value
被P - N + 1
移位,从而使AND
在位置P
处具有所需的位数和相应的1位数。由于两者都被移位,因此值从最低有效位开始,结果是位置P
处的N
位的值。
这样就避免了对所需的每个比特范围的数字和单个比特位置进行硬编码。
在您的示例中,您希望从0111000000000000
(28672
(中提取前3位,这将是位置P == 15
处的3位N == 3
,结果是011
(3
(。
一个简短的例子
下面的示例使用unsigned
作为类型,只要它在硬件上至少为2字节,就足以满足uint16_t
类型的要求。
#include <stdio.h>
#include <limits.h>
/** extract N bits from value starting at position P,
* counting from 0 for the least significant bit
*/
unsigned nbitsatp (unsigned value, unsigned N, unsigned P)
{
/* mask is N 1's bits */
unsigned mask = ~0u >> ((sizeof mask * CHAR_BIT) - N);
return (value >> (P - N + 1)) & mask;
}
int main (void) {
unsigned v, n, p;
fputs ("enter v, n, p : ", stdout); /* prompt for v, n, p */
/* read/validate positive int value */
if (scanf ("%u%u%u", &v, &n, &p) != 3) {
fputs ("error: invalid unsigned integer input.n", stderr);
return 1;
}
/* output result */
printf ("nvalue of %u bits at pos %u in %u is : %un",
n, p, v, nbitsatp (v, n, p));
}
示例使用/输出
您想要28672
:的位置15开始的3位的具体示例
$ ./bin/nbitsatp
enter v, n, p : 28672 3 15
value of 3 bits at pos 15 in 28672 is : 3
或者,让我们取位置15的前4位,0111
(7
(:
$ ./bin/nbitsatp
enter v, n, p : 28672 4 15
value of 4 bits at pos 15 in 28672 is : 7
或者从位置15开始的前5位:
$ ./bin/nbitsatp
enter v, n, p : 28672 5 15
value of 5 bits at pos 15 in 28672 is : 14
或者,在您的示例中,9个标志(9位,位于位置8(的值将全部为零:
$ ./bin/nbitsatp
enter v, n, p : 28672 9 8
value of 9 bits at pos 8 in 28672 is : 0
使用预先定义的宏检索想要的比特
使用nbitsatp()
函数检索感兴趣的位的一种方便方法是为要获得的每组位#define
一个宏。例如,为了获得数据偏移量的3位、保留的4位和9位标志集,您可以定义三个设置位数和位置的宏,从而允许您简单地将TCP标头值作为参数传递,例如
/* macros for 3-bit offset, 4-bit reserved, 9-bit flags */
#define HDR_OFFSET(TCPHDRVAL) nbitsatp ((TCPHDRVAL), 3, 15)
#define HDR_RESERVED(TCPHDRVAL) nbitsatp ((TCPHDRVAL), 4, 12)
#define HDR_FLAGS(TCPHDRVAL) nbitsatp ((TCPHDRVAL), 9, 8)
要获得所需的位,只需调用将TCP标头值作为参数传递的宏,例如
int main (void) {
unsigned v;
fputs ("enter TCP hdr value : ", stdout); /* prompt TCP HDR VAL */
/* read/validate TCP header value */
if (scanf ("%u", &v) != 1) {
fputs ("error: invalid unsigned integer input.n", stderr);
return 1;
}
/* output result */
printf ("n data offset bits : %un"
" reserved bits : %un"
" flag bits : %un",
HDR_OFFSET (v), HDR_RESERVED (v), HDR_FLAGS (v));
}
输出
$ /bin/nbitsatp_macro
enter TCP hdr value : 28672
data offset bits : 3
reserved bits : 8
flag bits : 0
如果需要,您可以以简单的方式输出3
(011
(、8
(1000
(的填充二进制表示,然后输出标志(000000000
(。请参阅此答案中的binprnpad((函数