访问C中char的位

时间:2011-12-21 02:58:10

标签: c

我有一个十六进制数0x37,它的二进制表示是0011 0111.如何访问二进制表示的前两位是“11”?如何使用位移或屏蔽来实现此目的?我可以一点一点地访问,但不能一次访问两位?

5 个答案:

答案 0 :(得分:15)

如果&您的号码为0x03,您将获得最后两位。

char c = 0x37;
char mask = 0x03;
char lastTwo = c & mask;

答案 1 :(得分: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;
}

答案 2 :(得分:5)

您也可以使用位字段来执行此操作。关于位字段的不好的部分是它们的工作原理在某种程度上依赖于编译器,但是如果你不需要将代码移植到许多架构上,那么它可能没问题。

这是一个在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;
}

联盟在那里,所以我们可以作为一个完整的字节访问它,或者通过位字段访问它。 #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];
}

传入一个值,然后传入你想要位的位,以及你想要多少位。因此,要从位位置2开始抓取6位,测试值为0x71,您可以调用:

x = bits(0x71, 2, 6);  // x is set to 0x1c

如果您不喜欢查找表,并且希望最小的代码执行此操作,则可以使用:

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));
}

您需要确保将掩码位声明为unsigned,因为如果它们已签名,则右移操作将进行符号扩展。

如果您将此函数的最后一个版本声明为内联,将其放入头文件中,并使用i_bitc_bits的常量值调用它,它将编译为最小代码以解决问题。 (例如,如果i_bit为0,则编译器知道>> 0不执行任何操作,并且不会生成该代码。如果编译器将c_bits视为常量,则可以完成在编译时转移mask_bits的所有工作。)但是你需要确保使用的assert()版本在发布版本中编译成任何东西,或者使用你自己的版本ASSERT()宏,让您的宏编译为空。

答案 3 :(得分:2)

你最好的选择就是使用比特屏蔽,正如你所提到的那样。 这样的事情可以解决问题:

x = 0x37;
y = x&0x30; //Mask out the first two bits of the higher nibble
y = y>>4;

答案 4 :(得分:1)

这是最容易使用的功能,我不希望别人在获得类似的东西之前长时间挣扎 -

char get_bits(char a, char no_of_bits)
{
    return a & ((no_of_bits << 1) - 1);
}
char a = 0x37;
char b = get_bits(a, 2);

希望将来帮助某人