如何在 C 中打印 1 位(不仅是最低有效位)?
How do I print 1 bit in C (not only the least significant bit)?
我正在开发一个发送 TCP header 的程序(模拟 3 次握手)。我有一个变量,这个变量包含数据偏移量(3 位)、保留(4 位)和 9 个标志(9 位)。我正在使用按位运算来设置位。问题是我怎样才能打印出每一位?
- 假设我存储从 2 到 4(从左到右)开始的数据偏移量:例如。 0111000000000000
如何打印这 3 位?变量:
u_int16_t reserved_ofs_flags;
我发现这个问题很相似,但答案只适用于最不重要的问题:How do I print one bit?
您可以使用 bitwise operations 来获取您想要的位。
例如:
unsigned int a = 22; // 10110 in binary
printf("%d\n", a & 1); // get the bit in the 1's place (0)
printf("%d\n", (a >> 1) & 1); // get the bit in the 2's place (1)
printf("%d\n", (a >> 2) & 1); // get the bit in the 4's place (1)
printf("%d\n", (a >> 3) & 1); // get the bit in the 8's place (0)
printf("%d\n", (a >> 4) & 1); // get the bit in the 16's place (1)
// 3 in decimal is 11 in binary
printf("%d\n", a & 3); // get the bits in the 1's and the 2's places
// (2 in decimal, 10 in binary)
printf("%d\n", (a >> 1) & 3); // get the bits in the 2's and the 4's places
// (3 in decimal, 11 in binary)
要从从位置 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;
如果我理解你的问题,并且你希望能够提取从位置 [=] 开始的 N
位数(从 1
到 sizeof(type) * CHAR_BIT
)的值22=](从 0
到 sizeof(type) * CHAR_BIT - 1
)然后你可以提取该位子集:
/** 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;
}
(注意: for 4-byte unsigned
, N
ranges from 1
to 32
while P
是 zero-based,范围从 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 : %u\n",
n, p, v, nbitsatp (v, n, p));
}
例子Use/Output
您想要从 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
使用 Pre-Defined 宏检索所需位
使用 nbitsatp()
函数检索您感兴趣的位的一种简便方法是 #define
为您要获取的每组位创建一个宏。例如,要获取数据偏移量的 3 位、保留的 4 位和 9 位标志集,您可以定义三个宏来设置位数和位置,从而允许您简单地传递 TCP Header 值作为参数,例如
/* 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 header 值作为参数传递的宏,例如
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 : %u\n"
" reserved bits : %u\n"
" flag bits : %u\n",
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() function in this answer
我正在开发一个发送 TCP header 的程序(模拟 3 次握手)。我有一个变量,这个变量包含数据偏移量(3 位)、保留(4 位)和 9 个标志(9 位)。我正在使用按位运算来设置位。问题是我怎样才能打印出每一位?
- 假设我存储从 2 到 4(从左到右)开始的数据偏移量:例如。 0111000000000000
如何打印这 3 位?变量:
u_int16_t reserved_ofs_flags;
我发现这个问题很相似,但答案只适用于最不重要的问题:How do I print one bit?
您可以使用 bitwise operations 来获取您想要的位。
例如:
unsigned int a = 22; // 10110 in binary
printf("%d\n", a & 1); // get the bit in the 1's place (0)
printf("%d\n", (a >> 1) & 1); // get the bit in the 2's place (1)
printf("%d\n", (a >> 2) & 1); // get the bit in the 4's place (1)
printf("%d\n", (a >> 3) & 1); // get the bit in the 8's place (0)
printf("%d\n", (a >> 4) & 1); // get the bit in the 16's place (1)
// 3 in decimal is 11 in binary
printf("%d\n", a & 3); // get the bits in the 1's and the 2's places
// (2 in decimal, 10 in binary)
printf("%d\n", (a >> 1) & 3); // get the bits in the 2's and the 4's places
// (3 in decimal, 11 in binary)
要从从位置 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;
如果我理解你的问题,并且你希望能够提取从位置 [=] 开始的 N
位数(从 1
到 sizeof(type) * CHAR_BIT
)的值22=](从 0
到 sizeof(type) * CHAR_BIT - 1
)然后你可以提取该位子集:
/** 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;
}
(注意: for 4-byte unsigned
, N
ranges from 1
to 32
while P
是 zero-based,范围从 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 : %u\n",
n, p, v, nbitsatp (v, n, p));
}
例子Use/Output
您想要从 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
使用 Pre-Defined 宏检索所需位
使用 nbitsatp()
函数检索您感兴趣的位的一种简便方法是 #define
为您要获取的每组位创建一个宏。例如,要获取数据偏移量的 3 位、保留的 4 位和 9 位标志集,您可以定义三个宏来设置位数和位置,从而允许您简单地传递 TCP Header 值作为参数,例如
/* 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 header 值作为参数传递的宏,例如
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 : %u\n"
" reserved bits : %u\n"
" flag bits : %u\n",
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() function in this answer