如何在C中打印1位(不仅仅是最低有效位)



我正在开发一个发送TCP报头的程序(模拟3握手(。我有一个变量,这个变量包含数据偏移(3位(、保留(4位(和9个标志(9位(。我正在使用逐位操作来设置位。问题是我如何打印每一个比特?

  1. 假设我存储从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类型中的位数
  • 如果Nunsigned类型中的位数,则更简单的表达式((1U << N) - 1)具有未定义的行为
  • 对于您的示例,P12N3,因此您可以写: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(从0sizeof(type) * CHAR_BIT - 1(开始的N比特数(从1sizeof(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字节unsignedN的范围从132,而P是从031的零基(

上面,mask从所有比特1(~0u(开始,然后偏移掉比特总数减去N(留下N的1个比特(。然后,valueP - 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((函数

相关内容

  • 没有找到相关文章

最新更新