我有一个十六进制数0x37,它的二进制表示是0011 0111.如何访问二进制表示的前两位是"11"?如何使用位移或屏蔽来实现此目的?我可以一点一点地访问,但不能一次访问两位?
das*_*ght 15
如果你&
的数字是0x03,你将得到最后两位.
char c = 0x37;
char mask = 0x03;
char lastTwo = c & mask;
Run Code Online (Sandbox Code Playgroud)
shi*_*kou 13
这是一个逐位访问它的示例:
#include <stdio.h>
int main()
{
char byte = 0x37;
int i;
for(i = 7; 0 <= i; i --)
printf("%d\n", (byte >> i) & 0x01);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
您也可以使用位字段来执行此操作.关于位字段的不好的部分是它们的工作原理在某种程度上依赖于编译器,但是如果你不需要将代码移植到许多架构中,也许它很好.
这是一个例子,写在Ubuntu Linux计算机上并使用GCC进行测试.
#include <assert.h>
#include <stdio.h>
#pragma pack(1)
typedef struct
{
unsigned int low2: 2; // 2 bits of the byte
unsigned int high6: 6; // 6 more bits of the byte
} MYBYTE;
typedef union
{
MYBYTE mybyte;
unsigned char b;
} MYUNION;
main()
{
MYUNION m;
assert(sizeof(m) == 1);
m.b = 0x03;
assert(m.mybyte.low2 == 0x03);
assert(m.mybyte.high6 == 0x00);
printf("low2 of 0x03 is: %u\n", m.mybyte.low2);
printf("high6 of 0x03 is: %u\n", m.mybyte.high6);
m.b = 0xff;
printf("low2 of 0x03 is: %u\n", m.mybyte.low2);
printf("high6 of 0x03 is: %u\n", m.mybyte.high6);
assert(m.mybyte.low2 == 0x03);
assert(m.mybyte.high6 == 0x3f);
m.mybyte.high6 = 0x1c;
m.mybyte.low2 = 0x01;
assert(m.b == 0x71);
printf("m.b is: 0x%02x\n", m.b);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
联盟在那里,所以我们可以作为一个完整的字节访问它,或者通过位字段访问它. #pragma pack(1)
是否确保位字段打包为一个字节,其中没有额外的"填充"位.(正如我之前所说,当您使用位字段时,您依赖于实现细节.)
但是看看访问你想要的位是多么简单和干净.您可以写入一个完整的字节并读出您想要的位,或者写入您想要的位并读出整个字节.
如果您打算使用这样的代码,那么最好有一些确保它正常工作的断言.
如果您不打算使用位字段,我建议您定义一个功能,为您进行移位和屏蔽,以确保您不会陷入困境.也许是这样的:
#include <limits.h>
static unsigned int _bit_masks[] =
{
0x00000000, 0x00000001, 0x00000003, 0x00000007,
0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f,
0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff,
0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff,
0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff,
0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff,
0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff,
0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff,
};
#define MIN(a, b) \
((a) < (b) ? (a) : (b))
unsigned int
bits(unsigned int x, unsigned int i_bit, unsigned int c_bits)
{
assert(UINT_MAX >= 4294967295U); // unsigned int must be at least 32-bit
assert(i_bit <= 31);
if (i_bit > 31)
return 0;
c_bits = MIN(c_bits, 32 - i_bit);
// shift-and-mask to grab the requested bits, and return those bits
return (x >> i_bit) & _bit_masks[c_bits];
}
Run Code Online (Sandbox Code Playgroud)
传入一个值,然后传入你想要位的位,以及你想要多少位.因此,要从位位置2开始获取6位,测试值为0x71,您可以调用:
x = bits(0x71, 2, 6); // x is set to 0x1c
Run Code Online (Sandbox Code Playgroud)
如果您不喜欢查找表,并且希望最小的代码执行此操作,则可以使用:
unsigned int
bits(unsigned int x, unsigned int i_bit, unsigned int c_bits)
{
const unsigned int mask_bits = 0xffffffff;
assert(UINT_MAX >= 4294967295U); // unsigned int must be at least 32-bit
assert(i_bit <= 31);
if (i_bit > 31)
return 0;
c_bits = MIN(c_bits, 32 - i_bit);
// shift-and-mask to grab the requested bits, and return those bits
return (x >> i_bit) & (mask_bits >> (32 - c_bits));
}
Run Code Online (Sandbox Code Playgroud)
您需要确保声明掩码位,unsigned
因为如果它们已签名,则右移操作将进行符号扩展.
如果你声明函数为内联的最后版本,把它放到头文件,并与常数值叫它i_bit
和c_bits
,它会向下编译到最少的代码来解决这个问题.(例如,如果i_bit
是0,编译器知道>> 0
没有做任何事情,只会不会产生该代码.如果编译器知道c_bits
作为一个常量,它可以做转移的各项工作mask_bits
在编译时).但你会需要确保你使用的版本assert()
在你的发布版本中编译成任何东西,否则使用你自己的ASSERT()
宏并使你的宏编译成任何东西.