2011-12-21 42 views
9

Tôi có một số hex 0x37 và biểu diễn nhị phân của nó là 0011 0111. Làm cách nào để truy cập 2 bit đầu tiên của biểu diễn nhị phân là "11"? Làm thế nào để sử dụng bit shifting hoặc masking để đạt được điều này? Tôi có thể truy cập từng chút một nhưng không thể truy cập từng bit một?Truy cập các bit trong một char trong C

Trả lời

12

Nếu bạn & số của mình với 0x03, bạn sẽ nhận được hai bit cuối cùng.

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

Đặt cược tốt nhất của bạn là sử dụng mặt nạ bit như bạn đã đề cập. Something như thế này nên làm như lừa:

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

Ở đây, tôi giả sử bạn muốn cô lập 11 từ "0011". Nếu bạn chỉ cần hai bit thứ tự thấp nhất, x & 0x03 sẽ hoạt động, như dasblinkenlight đã hiển thị. – zje

9

Đây là một mẫu truy cập vào nó từng chút một:

#include <stdio.h> 
int main() 
{ 
    char byte = 0x37; 
    int i; 

    for(i = 7; 0 <= i; i --) 
     printf("%d\n", (byte >> i) & 0x01); 

    return 0; 
} 
5

Bạn cũng có thể sử dụng bit lĩnh vực để làm điều này. Phần xấu về các trường bit là chính xác cách chúng hoạt động có phần phụ thuộc vào trình biên dịch, nhưng nếu bạn không cần phải chuyển mã của mình sang nhiều kiến ​​trúc, có lẽ nó là tốt.

Dưới đây là một ví dụ, được viết trên máy tính Ubuntu Linux và được kiểm tra bằng 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; 
} 

Công đoàn ở đó để chúng tôi có thể truy cập dưới dạng byte đầy đủ hoặc truy cập theo trường bit. #pragma pack(1) là có để đảm bảo rằng các trường bit đóng gói xuống một byte, không có thêm "đệm" bit trong đó. (Như tôi đã nói trước đây, bạn đang dựa vào chi tiết triển khai khi bạn sử dụng các trường bit.)

Nhưng hãy xem cách đơn giản và sạch sẽ là truy cập vào các bit bạn muốn. Bạn có thể viết toàn bộ một byte và đọc các bit bạn muốn, hoặc viết vào các bit bạn muốn và đọc toàn bộ byte.

Nếu bạn định sử dụng mã như thế này, bạn nên có một số lời khẳng định chắc chắn rằng nó hoạt động.

Nếu bạn không sử dụng các trường bit, tôi khuyên bạn nên xác định một chức năng mà dịch chuyển và che mặt cho bạn, để đảm bảo bạn không làm hỏng. Có thể một cái gì đó như thế này:

#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]; 
} 

Bạn chuyển một giá trị, sau đó vị trí bit bạn muốn bit và số bit bạn muốn. Vì vậy, để lấy 6 bit bắt đầu từ vị trí bit 2, với một giá trị thử nghiệm của 0x71 bạn có thể gọi:

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

Nếu bạn không thích các bảng tra cứu, và bạn muốn mã nhỏ nhất để làm điều này, bạn có thể sử dụng:

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

Bạn cần phải chắc chắn rằng các bit mặt nạ được khai báo unsigned bởi vì nếu họ được ký kết, các hoạt động phải thay đổi sẽ tiến hành ký-mở rộng.

Nếu bạn khai báo phiên bản mới nhất của hàm là nội tuyến, hãy đặt nó vào tệp tiêu đề và gọi nó với giá trị không đổi cho i_bitc_bits, nó sẽ biên dịch xuống mã tối thiểu để giải quyết vấn đề. (Ví dụ, nếu i_bit là 0, trình biên dịch biết rằng >> 0 không làm bất cứ điều gì và sẽ không tạo ra mã đó.Và nếu trình biên dịch biết c_bits là hằng số, nó có thể thực hiện tất cả công việc dịch mask_bits tại thời gian biên dịch.) Nhưng bạn sẽ cần phải đảm bảo rằng bạn đang sử dụng phiên bản assert() để biên dịch thành không có gì trong bản phát hành bản phát hành của bạn hoặc sử dụng macro ASSERT() của riêng bạn và làm cho macro của bạn biên dịch thành không có gì.

2

Đây là chức năng dễ sử dụng nhất, tôi không muốn người khác gặp khó khăn trong một thời gian dài trước khi nhận được một cái gì đó tương tự như sau -

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

Hy vọng nó sẽ giúp ai đó trong tương lai

Các vấn đề liên quan