Làm thế nào để bạn thiết lập, xóa và chuyển đổi một bit?


2569

Làm thế nào để bạn thiết lập, xóa và chuyển đổi một chút?


65
đọc: graphics.stanford.edu/~seander/bithacks.html và khi bạn sẽ làm chủ này, đọc cái này: realtimecollisiondetection.net/blog/?p=78
ugasoft

9
Bạn cũng có thể quan tâm đến việc kiểm tra Bit Twiddler , Bit Twiddling HacksThuật toán ma thuật tổng hợp .

Câu trả lời:


3599

Thiết lập một chút

Sử dụng toán tử bitwise OR ( |) để đặt bit.

number |= 1UL << n;

Điều đó sẽ thiết lập nbit thứ number. nnên bằng 0, nếu bạn muốn đặt 1bit st và cứ thế n-1, nếu bạn muốn đặt nbit thứ.

Sử dụng 1ULLnếu numberrộng hơn unsigned long; quảng cáo 1UL << nkhông xảy ra cho đến khi sau khi đánh giá 1UL << nhành vi không xác định của nó sẽ thay đổi nhiều hơn chiều rộng của a long. Điều tương tự áp dụng cho tất cả các ví dụ còn lại.

Dọn dẹp một chút

Sử dụng toán tử bitwise AND ( &) để xóa một chút.

number &= ~(1UL << n);

Điều đó sẽ xóa nbit thứ number. Bạn phải đảo ngược chuỗi bit với toán tử bitwise NOT ( ~), sau đó VÀ nó.

Đi bộ một chút

Toán tử XOR ( ^) có thể được sử dụng để chuyển đổi một chút.

number ^= 1UL << n;

Điều đó sẽ thay đổi nbit thứ number.

Kiểm tra một chút

Bạn đã không yêu cầu điều này, nhưng tôi cũng có thể thêm nó.

Để kiểm tra một chút, dịch chuyển số n sang phải, sau đó theo bit VÀ nó:

bit = (number >> n) & 1U;

Điều đó sẽ đặt giá trị của nbit thứ numbervào biến bit.

Thay đổi bit thứ n thành x

Đặt nbit thứ một 1hoặc 0có thể đạt được bằng cách sau khi triển khai C ++ bổ sung của 2:

number ^= (-x ^ number) & (1UL << n);

Bit nsẽ được đặt nếu x1và xóa nếu x0. Nếu xcó một số giá trị khác, bạn nhận được rác. x = !!xsẽ booleanize nó thành 0 hoặc 1.

Để làm cho điều này độc lập với hành vi phủ định bổ sung của 2 (trong đó -1có tất cả các bit được đặt, không giống như thực hiện C ++ bổ sung hoặc ký hiệu / cường độ 1), hãy sử dụng phủ định không dấu.

number ^= (-(unsigned long)x ^ number) & (1UL << n);

hoặc là

unsigned long newbit = !!x;    // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);

Nói chung, nên sử dụng các loại không dấu cho thao tác bit di động.

hoặc là

number = (number & ~(1UL << n)) | (x << n);

(number & ~(1UL << n))sẽ xóa nbit thứ và (x << n)sẽ đặt nbit thứ x.

Nói chung, một ý tưởng tốt là không sao chép / dán mã nói chung và rất nhiều người sử dụng các macro tiền xử lý (như wiki cộng đồng trả lời sâu hơn ) hoặc một số cách đóng gói.


128
Tôi muốn lưu ý rằng trên các nền tảng có hỗ trợ riêng cho bit set / Clear (ví dụ, bộ vi điều khiển AVR), trình biên dịch sẽ thường dịch 'myByte | = (1 << x)' sang tập lệnh gốc / xóa hướng dẫn bất cứ khi nào x một hằng số, ví dụ: (1 << 5) hoặc const unsign x = 5.
Aaron

52
bit = số & (1 << x); sẽ không đặt giá trị của bit x vào bit trừ khi bit có loại _Bool (<stdbool.h>). Mặt khác, bit = !! (số & (1 << x)); sẽ ..
Chris Young

23
tại sao bạn không đổi người cuối cùng thànhbit = (number >> x) & 1
aaronman

42
1là một intnghĩa đen, được ký kết. Vì vậy, tất cả các hoạt động ở đây hoạt động trên các số đã ký, không được xác định rõ bởi các tiêu chuẩn. Các tiêu chuẩn không đảm bảo hai phép bổ sung hoặc thay đổi số học nên sử dụng tốt hơn 1U.
Siyuan Ren

50
Tôi thích number = number & ~(1 << n) | (x << n);thay đổi bit thứ n thành x.
jiasli

459

Sử dụng Thư viện C ++ chuẩn : std::bitset<N>.

Hoặc phiên bản Boost : boost::dynamic_bitset.

Không cần phải tự cuộn:

#include <bitset>
#include <iostream>

int main()
{
    std::bitset<5> x;

    x[1] = 1;
    x[2] = 0;
    // Note x[0-4]  valid

    std::cout << x << std::endl;
}

[Alpha:] > ./a.out
00010

Phiên bản Boost cho phép một bitet có kích thước thời gian chạy so với một bitet có kích thước thời gian biên dịch thư viện chuẩn .


34
+1. Không phải std :: bitset có thể sử dụng được từ "C", nhưng vì tác giả đã gắn thẻ câu hỏi của anh ấy / cô ấy bằng "C ++", AFAIK, câu trả lời của bạn là tốt nhất ở đây ... std :: vector <bool> là một cách khác, nếu ai đó biết ưu và nhược điểm của nó
paercebal

23
@andrewdotnich: vector <bool> là (không may) là một chuyên môn lưu trữ các giá trị dưới dạng bit. Xem gotw.ca/publications/mill09.htm để biết thêm thông tin ...
Niklas

71
Có lẽ không ai đề cập đến nó bởi vì điều này đã được gắn thẻ nhúng. Trong hầu hết các hệ thống nhúng, bạn tránh STL như bệnh dịch hạch. Và hỗ trợ tăng cường có khả năng là một loài chim rất hiếm thấy trong số hầu hết các trình biên dịch nhúng.
Lundin

17
@Martin Nó rất đúng. Bên cạnh các trình diệt hiệu năng cụ thể như STL và các mẫu, nhiều hệ thống nhúng thậm chí còn tránh toàn bộ thư viện tiêu chuẩn, bởi vì chúng là một nỗi đau để xác minh. Hầu hết các nhánh nhúng đang tuân theo các tiêu chuẩn như MISRA, yêu cầu các công cụ phân tích mã tĩnh (bất kỳ chuyên gia phần mềm nào cũng nên sử dụng các công cụ đó btw, không chỉ các folks nhúng). Nói chung mọi người có nhiều việc phải làm hơn là chạy phân tích tĩnh trong toàn bộ thư viện tiêu chuẩn - nếu mã nguồn của nó thậm chí có sẵn cho họ trên trình biên dịch cụ thể.
Lundin

37
@Lundin: Tuyên bố của bạn quá rộng (do đó vô dụng để tranh luận). Tôi chắc chắn rằng tôi có thể tìm thấy tình huống là chúng là sự thật. Điều này không thay đổi điểm ban đầu của tôi. Cả hai lớp này đều hoàn toàn tốt để sử dụng trong các hệ thống nhúng (và tôi biết thực tế là chúng được sử dụng). Điểm ban đầu của bạn về STL / Boost không được sử dụng trên các hệ thống nhúng cũng là sai. Tôi chắc chắn có những hệ thống không sử dụng chúng và thậm chí cả những hệ thống sử dụng chúng đều được sử dụng một cách thận trọng nhưng nói rằng chúng không được sử dụng là không đúng (vì có những hệ thống được sử dụng).
Martin York

248

Tùy chọn khác là sử dụng các trường bit:

struct bits {
    unsigned int a:1;
    unsigned int b:1;
    unsigned int c:1;
};

struct bits mybits;

định nghĩa trường 3 bit (thực ra, đó là ba feld 1 bit). Hoạt động bit bây giờ trở nên đơn giản hơn một chút (haha):

Để thiết lập hoặc xóa một chút:

mybits.b = 1;
mybits.c = 0;

Để chuyển đổi một chút:

mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1;  /* all work */

Kiểm tra một chút:

if (mybits.c)  //if mybits.c is non zero the next line below will execute

Điều này chỉ hoạt động với các trường bit kích thước cố định. Nếu không, bạn phải sử dụng các kỹ thuật xoay vòng bit được mô tả trong các bài viết trước.


68
Tôi luôn thấy sử dụng bitfield là một ý tưởng tồi. Bạn không có quyền kiểm soát thứ tự các bit được phân bổ (từ đỉnh hoặc đáy), điều này khiến cho không thể tuần tự hóa giá trị theo cách ổn định / di động, ngoại trừ bit tại một thời điểm. Cũng không thể trộn số học bit DIY với bitfield, ví dụ như tạo mặt nạ kiểm tra nhiều bit cùng một lúc. Tất nhiên bạn có thể sử dụng && và hy vọng trình biên dịch sẽ tối ưu hóa chính xác ...
R .. GitHub DỪNG GIÚP ICE

34
Các trường bit rất tệ theo nhiều cách, tôi gần như có thể viết một cuốn sách về nó. Trong thực tế, tôi gần như phải làm điều đó cho một chương trình trường bit cần tuân thủ MISRA-C. MISRA-C thực thi tất cả các hành vi được xác định thực hiện phải được ghi lại, vì vậy tôi đã kết thúc bằng cách viết một bài luận về tất cả mọi thứ có thể sai trong các trường bit. Thứ tự bit, endianess, bit đệm, byte đệm, các vấn đề căn chỉnh khác, chuyển đổi loại ngầm định và rõ ràng sang và từ một trường bit, UB nếu int không được sử dụng, v.v. Thay vào đó, hãy sử dụng các toán tử bitwise để có ít lỗi hơn và mã di động. Các trường bit là hoàn toàn dư thừa.
Lundin

44
Giống như hầu hết các tính năng ngôn ngữ, các trường bit có thể được sử dụng chính xác hoặc chúng có thể bị lạm dụng. Nếu bạn cần đóng gói một vài giá trị nhỏ vào một int, các trường bit có thể rất hữu ích. Mặt khác, nếu bạn bắt đầu đưa ra các giả định về cách các trường bit ánh xạ tới thực tế có chứa int, bạn chỉ đang yêu cầu sự cố.
Ferruccio

4
@endolith: Đó sẽ không phải là một ý tưởng tốt. Bạn có thể làm cho nó hoạt động, nhưng nó không nhất thiết phải di động đến một bộ xử lý khác, hoặc một trình biên dịch khác hoặc thậm chí cho bản phát hành tiếp theo của cùng một trình biên dịch.
Ferruccio

3
@Yasky và Ferruccio nhận được các câu trả lời khác nhau cho sizeof () cho phương pháp này sẽ minh họa các vấn đề với khả năng tương thích không chỉ trên các trình biên dịch mà trên cả phần cứng. Đôi khi chúng tôi tự lừa mình rằng chúng tôi đã giải quyết các vấn đề này bằng ngôn ngữ hoặc thời gian chạy được xác định nhưng nó thực sự đi đến 'nó có hoạt động trên máy của tôi không?'. Các bạn nhúng có sự tôn trọng của tôi (và thông cảm).
Kelly S. Pháp

181

Tôi sử dụng các macro được xác định trong tệp tiêu đề để xử lý tập bit và xóa:

/* a=target variable, b=bit number to act upon 0-n */
#define BIT_SET(a,b) ((a) |= (1ULL<<(b)))
#define BIT_CLEAR(a,b) ((a) &= ~(1ULL<<(b)))
#define BIT_FLIP(a,b) ((a) ^= (1ULL<<(b)))
#define BIT_CHECK(a,b) (!!((a) & (1ULL<<(b))))        // '!!' to make sure this returns 0 or 1

/* x=target variable, y=mask */
#define BITMASK_SET(x,y) ((x) |= (y))
#define BITMASK_CLEAR(x,y) ((x) &= (~(y)))
#define BITMASK_FLIP(x,y) ((x) ^= (y))
#define BITMASK_CHECK_ALL(x,y) (((x) & (y)) == (y))   // warning: evaluates y twice
#define BITMASK_CHECK_ANY(x,y) ((x) & (y))

17
Uh tôi nhận ra đây là một bài viết 5 năm tuổi nhưng không có sự trùng lặp đối số trong bất kỳ macro nào, Dan
Robert Kelly

11
BITMASK_CHECK(x,y) ((x) & (y))phải là ((x) & (y)) == (y)cách khác, nó trả về kết quả không chính xác trên mặt nạ multibit (ví dụ 5so với 3) / * Xin chào tất cả các
gra graiggers :)

7
1nên (uintmax_t)1hoặc tương tự trong trường hợp bất kỳ ai cố gắng sử dụng các macro này trên một loại longhoặc lớn hơn
MM

2
BITMASK_CHECK_ALL(x,y)có thể được thực hiện như!~((~(y))|(x))
Handy999

3
@ Handy999 Dễ dàng hơn một chút để biết lý do tại sao điều đó hoạt động sau khi áp dụng luật của De Morgan và sắp xếp lại để có được!(~(x) & (y))
Tavian Barnes

114

Đôi khi đáng để sử dụng một enumđể đặt tên cho các bit:

enum ThingFlags = {
  ThingMask  = 0x0000,
  ThingFlag0 = 1 << 0,
  ThingFlag1 = 1 << 1,
  ThingError = 1 << 8,
}

Sau đó sử dụng tên sau này. Tức là viết

thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}

để thiết lập, xóa và kiểm tra. Bằng cách này bạn ẩn các số ma thuật khỏi phần còn lại của mã của bạn.

Ngoài ra, tôi tán thành giải pháp của Jeremy.


1
Thay phiên, bạn có thể thực hiện một clearbits()chức năng thay vì &= ~. Tại sao bạn sử dụng một enum cho điều này? Tôi nghĩ rằng chúng là để tạo ra một loạt các biến duy nhất có giá trị tùy ý ẩn, nhưng bạn đang gán một giá trị xác định cho mỗi biến. Vì vậy, lợi ích so với chỉ định nghĩa chúng là các biến?
endolith

4
@endolith: Việc sử dụng enums cho các bộ hằng số liên quan trở lại một chặng đường dài trong lập trình c. Tôi nghi ngờ rằng với các trình biên dịch hiện đại, lợi thế duy nhất hơn const shorthoặc bất cứ điều gì là chúng được nhóm rõ ràng với nhau. Và khi bạn muốn họ cho một cái gì đó khác hơn bitmasks bạn sẽ có được đánh số tự động. Tất nhiên, trong c ++, chúng cũng tạo thành các loại riêng biệt giúp bạn kiểm tra lỗi tĩnh thêm một chút.
dmckee --- ex-moderator mèo con

Bạn sẽ nhận được các hằng số enum không xác định nếu bạn không xác định hằng số cho mỗi giá trị có thể có của các bit. Là gì enum ThingFlagsgiá trị cho ThingError|ThingFlag1, cho ví dụ?
Luis Colorado

6
Nếu bạn sử dụng phương pháp này, xin lưu ý rằng hằng số enum luôn thuộc loại đã ký int. Điều này có thể gây ra tất cả các lỗi tinh vi do quảng cáo số nguyên ẩn hoặc hoạt động theo bit trên các loại đã ký. thingstate = ThingFlag1 >> 1ví dụ sẽ gọi hành vi xác định thực hiện. thingstate = (ThingFlag1 >> x) << ycó thể gọi hành vi không xác định. Và như thế. Để an toàn, luôn luôn chuyển sang loại không dấu.
Lundin

1
@Lundin: Kể từ C ++ 11, bạn có thể đặt loại liệt kê cơ bản, ví dụ: enum My16Bits: unsigned short { ... };
Aiken Drum

47

Từ bitops của snip-c.zip.h:

/*
**  Bit set, clear, and test operations
**
**  public domain snippet by Bob Stout
*/

typedef enum {ERROR = -1, FALSE, TRUE} LOGICAL;

#define BOOL(x) (!(!(x)))

#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))

OK, hãy phân tích mọi thứ ...

Biểu thức phổ biến mà bạn dường như gặp vấn đề trong tất cả những điều này là "(1L << (posn))". Tất cả điều này là tạo một mặt nạ với một bit duy nhất và nó sẽ hoạt động với bất kỳ loại số nguyên nào. Đối số "posn" chỉ định vị trí mà bạn muốn bit. Nếu posn == 0, thì biểu thức này sẽ ước tính thành:

0000 0000 0000 0000 0000 0000 0000 0001 binary.

Nếu posn == 8, nó sẽ ước tính:

0000 0000 0000 0000 0000 0001 0000 0000 binary.

Nói cách khác, nó chỉ đơn giản tạo ra một trường 0 'với 1 tại vị trí đã chỉ định. Phần khó khăn duy nhất là trong macro BitClr () nơi chúng ta cần đặt 0 bit đơn trong trường 1 giây. Điều này được thực hiện bằng cách sử dụng phần bù 1 của cùng biểu thức như được biểu thị bởi toán tử dấu ngã (~).

Khi mặt nạ được tạo, nó được áp dụng cho đối số như bạn đề xuất, bằng cách sử dụng các toán tử bitwise và (&) hoặc (|) và xor (^). Vì mặt nạ có kiểu dài, các macro sẽ hoạt động tốt như trên char, short, int hoặc long.

Điểm mấu chốt là đây là một giải pháp chung cho toàn bộ lớp vấn đề. Tất nhiên, có thể và thậm chí thích hợp để viết lại tương đương với bất kỳ macro nào có giá trị mặt nạ rõ ràng mỗi khi bạn cần, nhưng tại sao lại làm như vậy? Hãy nhớ rằng, sự thay thế macro xảy ra trong bộ tiền xử lý và do đó mã được tạo sẽ phản ánh thực tế rằng các giá trị được trình biên dịch coi là không đổi - tức là sử dụng các macro tổng quát như là "phát minh lại bánh xe" mỗi khi bạn cần làm thao tác bit.

Không tin tưởng? Đây là một số mã kiểm tra - Tôi đã sử dụng Watcom C với tối ưu hóa hoàn toàn và không sử dụng _cdecl để việc tháo gỡ kết quả sẽ sạch nhất có thể:

---- [TEST.C] ----------------------------------------- -----------------------

#define BOOL(x) (!(!(x)))

#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))

int bitmanip(int word)
{
      word = BitSet(word, 2);
      word = BitSet(word, 7);
      word = BitClr(word, 3);
      word = BitFlp(word, 9);
      return word;
}

---- [TEST.OUT (tháo rời)] -------------------------------------- ---------

Module: C:\BINK\tst.c
Group: 'DGROUP' CONST,CONST2,_DATA,_BSS

Segment: _TEXT  BYTE   00000008 bytes  
 0000  0c 84             bitmanip_       or      al,84H    ; set bits 2 and 7
 0002  80 f4 02                          xor     ah,02H    ; flip bit 9 of EAX (bit 1 of AH)
 0005  24 f7                             and     al,0f7H
 0007  c3                                ret     

No disassembly errors

---- [vây] ------------------------------------------- ----------------------


3
2 điều về điều này: (1) trong việc kiểm tra các macro của bạn, một số có thể tin sai rằng các macro thực sự đặt / xóa / lật các bit trong đối số, tuy nhiên không có sự phân công; (2) test.c của bạn chưa hoàn thành; Tôi nghi ngờ nếu bạn chạy nhiều trường hợp hơn, bạn sẽ gặp sự cố (bài tập của độc giả)
Dan

19
-1 Đây chỉ là sự xáo trộn kỳ lạ. Không bao giờ phát minh lại ngôn ngữ C bằng cách ẩn đi cú pháp ngôn ngữ đằng sau các macro, đó là một thực tế rất xấu. Sau đó, một số điều kỳ lạ: đầu tiên, 1L được ký, có nghĩa là tất cả các hoạt động bit sẽ được thực hiện trên một loại đã ký. Mọi thứ được chuyển đến các macro này sẽ trở lại như đã ký từ lâu. Không tốt. Thứ hai, điều này sẽ hoạt động rất kém hiệu quả trên các CPU nhỏ hơn vì nó thực thi lâu khi các hoạt động có thể đã ở mức int. Thứ ba, các macro giống như chức năng là gốc rễ của mọi tội lỗi: bạn không có loại an toàn nào. Ngoài ra, nhận xét trước về không có bài tập là rất hợp lệ.
Lundin

2
Điều này sẽ thất bại nếu arglong long. 1Lcần phải là loại rộng nhất có thể, vì vậy (uintmax_t)1. (Bạn có thể thoát khỏi 1ull)
MM

Bạn đã tối ưu hóa cho kích thước mã? Trên các CPU chính của Intel, bạn sẽ nhận được các quầy đăng ký một phần khi đọc AX hoặc EAX sau khi hàm này trả về, vì nó ghi các thành phần 8 bit của EAX. (Đó là tốt trên các CPU AMD, hoặc những người khác mà không đổi tên đăng ký một phần tách biệt với đầy đủ vào sổ đăng ký. Haswell / Skylake không đổi tên AL riêng biệt, nhưng họ làm AH đổi tên. ).
Peter Cordes

37

Sử dụng các toán tử bitwise: & |

Để đặt bit cuối cùng trong 000b:

foo = foo | 001b

Để kiểm tra bit cuối cùng trong foo:

if ( foo & 001b ) ....

Để xóa bit cuối cùng trong foo:

foo = foo & 110b

Tôi đã sử dụng XXXbcho rõ ràng. Bạn có thể sẽ làm việc với đại diện HEX, tùy thuộc vào cấu trúc dữ liệu mà bạn đang đóng gói bit.


7
Không có ký hiệu nhị phân trong C. Hằng số nguyên nhị phân là phần mở rộng không chuẩn.
Lundin

Sử dụng XOR để chuyển đổi một chút:foo = foo ^ MY_MASK
Peter L

Sử dụng KHÔNG để đảo ngược mặt nạ để làm rõ:foo = foo & ~MY_MASK
Peter L

32

Đối với người mới bắt đầu, tôi muốn giải thích thêm một chút với một ví dụ:

Thí dụ:

value is 0x55;
bitnum : 3rd.

Các &nhà điều hành được sử dụng kiểm tra các bit:

0101 0101
&
0000 1000
___________
0000 0000 (mean 0: False). It will work fine if the third bit is 1 (then the answer will be True)

Chuyển đổi hoặc lật:

0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)

| toán tử: đặt bit

0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)

26

Đây là macro số học bit yêu thích của tôi, hoạt động cho bất kỳ loại mảng số nguyên không dấu nào từ unsigned chartối đa size_t(là loại lớn nhất cần hiệu quả để làm việc):

#define BITOP(a,b,op) \
 ((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))

Để thiết lập một chút:

BITOP(array, bit, |=);

Để xóa một chút:

BITOP(array, bit, &=~);

Để chuyển đổi một chút:

BITOP(array, bit, ^=);

Để kiểm tra một chút:

if (BITOP(array, bit, &)) ...

Vân vân.


5
Rất tốt để đọc nhưng người ta nên nhận thức được các tác dụng phụ có thể xảy ra. Sử dụng BITOP(array, bit++, |=);trong một vòng lặp rất có thể sẽ không làm những gì người gọi muốn.
trả trước

Thật. =) Một biến thể bạn có thể thích là để tách nó thành 2 macro, 1 để giải quyết các phần tử mảng và một cho chuyển các bit vào đúng vị trí, ala BITCELL(a,b) |= BITMASK(a,b);(cả mất anhư một tham số để xác định kích thước, nhưng sau này sẽ không bao giờ đánh giá atừ nó chỉ xuất hiện trong sizeof).
R .. GitHub DỪNG GIÚP ICE

1
@R .. Câu trả lời này thực sự cũ, nhưng có lẽ tôi thích một hàm hơn cho macro trong trường hợp này.
PC Luddite

Nhỏ: thứ 3 (size_t)diễn viên dường như có mặt ở đó chỉ để đảm bảo một số toán unsigned với %. Có thể (unsigned)có.
chux - Phục hồi Monica

Không (size_t)(b)/(8*sizeof *(a))cần thiết có thể thu hẹp btrước khi phân chia. Chỉ có một vấn đề với mảng bit rất lớn. Vẫn là một macro thú vị.
chux - Phục hồi Monica

25

Vì điều này được gắn thẻ "nhúng" Tôi sẽ cho rằng bạn đang sử dụng một vi điều khiển. Tất cả các đề xuất trên là hợp lệ và công việc (đọc-sửa-ghi, đoàn thể, cấu trúc, v.v.).

Tuy nhiên, trong quá trình gỡ lỗi dựa trên dao động, tôi đã rất ngạc nhiên khi thấy rằng các phương thức này có chi phí đáng kể trong chu kỳ CPU so với việc ghi giá trị trực tiếp vào các thanh ghi PORTnSET / PORTnCLEAR của micro tạo ra sự khác biệt thực sự khi có các vòng lặp chặt chẽ / cao -frequency ISR's ghim ghim.

Đối với những người không quen thuộc: Trong ví dụ của tôi, micro có PORTn trạng thái pin chung chung phản ánh các chân đầu ra, do đó, PORTn | = BIT_TO_SET dẫn đến việc đọc-sửa đổi-ghi vào thanh ghi đó. Tuy nhiên, các thanh ghi PORTnSET / PORTnCLEAR lấy '1' có nghĩa là "vui lòng tạo bit này 1" (SET) hoặc "vui lòng làm cho bit này bằng 0" (XÓA) và '0' có nghĩa là "để yên pin". do đó, bạn kết thúc với hai địa chỉ cổng tùy thuộc vào việc bạn đang cài đặt hoặc xóa bit (không phải lúc nào cũng thuận tiện) nhưng phản ứng nhanh hơn nhiều và mã được lắp ráp nhỏ hơn.


Micro là Coldfire MCF52259, sử dụng C trong Codewar Warrior. Nhìn vào trình phân tách / asm là một bài tập hữu ích vì nó cho thấy tất cả các bước mà CPU phải trải qua để thực hiện ngay cả thao tác cơ bản nhất. <br> Chúng tôi cũng phát hiện ra các hướng dẫn sử dụng CPU khác trong các vòng lặp quan trọng về thời gian - ràng buộc một biến bằng cách thực hiện var% = max_val tốn một đống chu kỳ CPU mỗi lần, trong khi thực hiện if (var> max_val) var- = max_val chỉ sử dụng một vài chỉ dẫn <br> Một hướng dẫn tốt cho một vài thủ thuật khác có ở đây: codeproject.com/Articles/6154/ Khăn
John U

Thậm chí quan trọng hơn, các thanh ghi I / O được ánh xạ bộ nhớ của người trợ giúp cung cấp một cơ chế để cập nhật nguyên tử. Đọc / sửa đổi / ghi có thể rất tệ nếu trình tự bị gián đoạn.
Ben Voigt

1
Hãy nhớ rằng tất cả các thanh ghi cổng sẽ được định nghĩa volatilevà do đó trình biên dịch không thể thực hiện bất kỳ tối ưu hóa nào trên mã liên quan đến các thanh ghi đó. Do đó, cách tốt nhất là tháo rời mã như vậy và xem cách nó bật ra ở cấp độ trình biên dịch.
Lundin

24

Cách tiếp cận bitfield có những lợi thế khác trong lĩnh vực nhúng. Bạn có thể định nghĩa một cấu trúc ánh xạ trực tiếp lên các bit trong một thanh ghi phần cứng cụ thể.

struct HwRegister {
    unsigned int errorFlag:1;  // one-bit flag field
    unsigned int Mode:3;       // three-bit mode field
    unsigned int StatusCode:4;  // four-bit status code
};

struct HwRegister CR3342_AReg;

Bạn cần lưu ý về thứ tự đóng gói bit - Tôi nghĩ đó là MSB trước tiên, nhưng điều này có thể phụ thuộc vào việc triển khai. Ngoài ra, xác minh cách trình biên dịch của bạn xử lý các trường vượt qua ranh giới byte.

Sau đó, bạn có thể đọc, viết, kiểm tra các giá trị riêng lẻ như trước đây.


2
Khá nhiều thứ về các trường bit được xác định theo triển khai. Ngay cả khi bạn quản lý để tìm hiểu tất cả các chi tiết liên quan đến cách trình biên dịch cụ thể của bạn thực hiện chúng, thì việc sử dụng chúng trong mã của bạn chắc chắn sẽ làm cho nó không khả chuyển.
Lundin

1
@Lundin - Đúng, nhưng hệ thống nhúng bit (đặc biệt là trong các thanh ghi phần cứng, đó là câu trả lời của tôi liên quan đến) dù sao cũng sẽ không thể mang theo một cách hữu ích.
Roddy

1
Không phải giữa các CPU hoàn toàn khác nhau có lẽ. Nhưng rất có thể bạn muốn nó có thể di động giữa các trình biên dịch và giữa các dự án khác nhau. Và có rất nhiều "bit-fiddling" nhúng không liên quan đến phần cứng, chẳng hạn như mã hóa / giải mã giao thức dữ liệu.
Lundin

... Và nếu bạn có thói quen sử dụng các trường bit khi lập trình nhúng, bạn sẽ thấy mã X86 của mình chạy nhanh hơn và cũng gọn hơn. Không phải trong các điểm chuẩn đơn giản, nơi bạn có toàn bộ cỗ máy để phá vỡ điểm chuẩn, mà trong môi trường đa tác vụ trong thế giới thực, nơi các chương trình cạnh tranh để lấy tài nguyên. Lợi thế CISC - với mục tiêu thiết kế ban đầu là bù cho CPU nhanh hơn bus và bộ nhớ chậm.

20

Kiểm tra một chút tại một vị trí tùy ý trong một biến loại tùy ý:

#define bit_test(x, y)  ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )

Sử dụng mẫu:

int main(void)
{
    unsigned char arr[8] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF };

    for (int ix = 0; ix < 64; ++ix)
        printf("bit %d is %d\n", ix, bit_test(arr, ix));

    return 0;
}

Ghi chú: Điều này được thiết kế để nhanh chóng (do tính linh hoạt của nó) và không phân nhánh. Nó dẫn đến mã máy SPARC hiệu quả khi biên dịch Sun Studio 8; Tôi cũng đã thử nghiệm nó bằng MSVC ++ 2008 trên amd64. Có thể tạo các macro tương tự để thiết lập và xóa bit. Sự khác biệt chính của giải pháp này so với nhiều giải pháp khác ở đây là nó hoạt động cho bất kỳ vị trí nào trong hầu hết mọi loại biến.


20

Tổng quát hơn, đối với bitmap có kích thước tùy ý:

#define BITS 8
#define BIT_SET(  p, n) (p[(n)/BITS] |=  (0x80>>((n)%BITS)))
#define BIT_CLEAR(p, n) (p[(n)/BITS] &= ~(0x80>>((n)%BITS)))
#define BIT_ISSET(p, n) (p[(n)/BITS] &   (0x80>>((n)%BITS)))

2
CHAR_BITđã được xác định bởi limits.h, bạn không cần phải tự đặt BITS(và thực tế là bạn làm cho mã của mình tệ hơn bằng cách làm như vậy)
MM

14

Chương trình này là để thay đổi bất kỳ bit dữ liệu nào từ 0 thành 1 hoặc 1 thành 0:

{
    unsigned int data = 0x000000F0;
    int bitpos = 4;
    int bitvalue = 1;
    unsigned int bit = data;
    bit = (bit>>bitpos)&0x00000001;
    int invbitvalue = 0x00000001&(~bitvalue);
    printf("%x\n",bit);

    if (bitvalue == 0)
    {
        if (bit == 0)
            printf("%x\n", data);
        else
        {
             data = (data^(invbitvalue<<bitpos));
             printf("%x\n", data);
        }
    }
    else
    {
        if (bit == 1)
            printf("elseif %x\n", data);
        else
        {
            data = (data|(bitvalue<<bitpos));
            printf("else %x\n", data);
        }
    }
}

14

Nếu bạn đang thực hiện nhiều thao tác xoay vòng, bạn có thể muốn sử dụng mặt nạ sẽ giúp mọi việc nhanh hơn. Các chức năng sau rất nhanh và vẫn linh hoạt (chúng cho phép xoay một chút trong các bản đồ bit có kích thước bất kỳ).

const unsigned char TQuickByteMask[8] =
{
   0x01, 0x02, 0x04, 0x08,
   0x10, 0x20, 0x40, 0x80,
};


/** Set bit in any sized bit mask.
 *
 * @return    none
 *
 * @param     bit    - Bit number.
 * @param     bitmap - Pointer to bitmap.
 */
void TSetBit( short bit, unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;        // Index to byte.
    n = bit % 8;        // Specific bit in byte.

    bitmap[x] |= TQuickByteMask[n];        // Set bit.
}


/** Reset bit in any sized mask.
 *
 * @return  None
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
void TResetBit( short bit, unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;        // Index to byte.
    n = bit % 8;        // Specific bit in byte.

    bitmap[x] &= (~TQuickByteMask[n]);    // Reset bit.
}


/** Toggle bit in any sized bit mask.
 *
 * @return   none
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
void TToggleBit( short bit, unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;        // Index to byte.
    n = bit % 8;        // Specific bit in byte.

    bitmap[x] ^= TQuickByteMask[n];        // Toggle bit.
}


/** Checks specified bit.
 *
 * @return  1 if bit set else 0.
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
short TIsBitSet( short bit, const unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;    // Index to byte.
    n = bit % 8;    // Specific bit in byte.

    // Test bit (logigal AND).
    if (bitmap[x] & TQuickByteMask[n])
        return 1;

    return 0;
}


/** Checks specified bit.
 *
 * @return  1 if bit reset else 0.
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
short TIsBitReset( short bit, const unsigned char *bitmap)
{
    return TIsBitSet(bit, bitmap) ^ 1;
}


/** Count number of bits set in a bitmap.
 *
 * @return   Number of bits set.
 *
 * @param    bitmap - Pointer to bitmap.
 * @param    size   - Bitmap size (in bits).
 *
 * @note    Not very efficient in terms of execution speed. If you are doing
 *        some computationally intense stuff you may need a more complex
 *        implementation which would be faster (especially for big bitmaps).
 *        See (http://graphics.stanford.edu/~seander/bithacks.html).
 */
int TCountBits( const unsigned char *bitmap, int size)
{
    int i, count = 0;

    for (i=0; i<size; i++)
        if (TIsBitSet(i, bitmap))
            count++;

    return count;
}

Lưu ý, để đặt bit 'n' trong số nguyên 16 bit, bạn làm như sau:

TSetBit( n, &my_int);

Tùy thuộc vào bạn để đảm bảo rằng số bit nằm trong phạm vi của bản đồ bit mà bạn vượt qua. Lưu ý rằng đối với các bộ xử lý endian nhỏ mà byte, từ, dwords, qwords, v.v., ánh xạ chính xác với nhau trong bộ nhớ (lý do chính khiến các bộ xử lý endian nhỏ 'tốt hơn' so với bộ xử lý endian lớn, ah, tôi cảm thấy một cuộc chiến nảy lửa sắp xảy ra trên...).


2
Không sử dụng bảng cho một chức năng có thể được thực hiện với một toán tử. TQuickByteMask [n] tương đương với (1 << n). Ngoài ra, làm cho lập luận của bạn ngắn là một ý tưởng rất xấu. / Và% thực sự sẽ là một phép chia, không phải bithift / bitwise và, bởi vì phép chia được ký bởi lũy thừa 2 không thể được thực hiện theo chiều bit. Bạn nên làm cho kiểu đối số không dấu int!
R .. GitHub DỪNG GIÚP ICE

Điểm này là gì? Nó chỉ làm cho mã chậm hơn và khó đọc hơn? Tôi không thể thấy một lợi thế duy nhất với nó. 1u << n dễ đọc hơn đối với các lập trình viên C và hy vọng có thể được dịch thành một lệnh CPU đánh dấu đồng hồ duy nhất. Mặt khác, phân chia của bạn sẽ được dịch thành khoảng 10 tick, hoặc thậm chí tệ đến 100 tick, tùy thuộc vào mức độ kém của kiến ​​trúc cụ thể xử lý phân chia. Đối với tính năng bitmap, sẽ có ý nghĩa hơn khi có bảng tra cứu dịch từng chỉ mục bit thành chỉ mục byte, để tối ưu hóa tốc độ.
Lundin

2
Đối với endian lớn / nhỏ, endian lớn sẽ ánh xạ các số nguyên và dữ liệu thô (ví dụ chuỗi) theo cùng một cách: msb từ trái sang phải sang lsb trong toàn bộ bitmap. Mặc dù endian nhỏ sẽ ánh xạ các số nguyên từ trái sang phải như 7-0, 15-8, 23-18, 31-24, nhưng dữ liệu thô vẫn là msb từ trái sang phải sang lsb. Vì vậy, làm thế nào ít endian là tốt hơn cho thuật toán cụ thể của bạn là hoàn toàn vượt quá tôi, nó dường như ngược lại.
Lundin

2
@R .. Một bảng có thể hữu ích nếu plattform bạn không thể thay đổi một cách hiệu quả, giống như vi mạch cũ mcu, nhưng tất nhiên sau đó các bộ phận trong mẫu là absolutly không hiệu quả
Jeb

12

Dùng cái này:

int ToggleNthBit ( unsigned char n, int num )
{
    if(num & (1 << n))
        num &= ~(1 << n);
    else
        num |= (1 << n);

    return num;
}

5
Vâng, nó sử dụng phân nhánh không hiệu quả.
asdf

3
@asdf Công việc của nhà biên dịch là tạo ra nhị phân hiệu quả nhất, công việc của lập trình viên là viết mã rõ ràng
MM

3
Đây là một minh chứng tốt về thử nghiệm, thiết lập và xóa một bit cụ thể. Tuy nhiên, đó là một cách tiếp cận rất tệ để thay đổi một chút.
Ben Voigt

10

Mở rộng về bitsetcâu trả lời:

#include <iostream>
#include <bitset>
#include <string>

using namespace std;
int main() {
  bitset<8> byte(std::string("10010011");

  // Set Bit
  byte.set(3); // 10010111

  // Clear Bit
  byte.reset(2); // 10010101

  // Toggle Bit
  byte.flip(7); // 00010101

  cout << byte << endl;

  return 0;
}

10

Nếu bạn muốn thực hiện tất cả thao tác này với lập trình C trong nhân Linux thì tôi khuyên bạn nên sử dụng các API tiêu chuẩn của nhân Linux.

Xem https://www.kernel.org/doc/htmldocs/kernel-api/ch02s03.html

set_bit  Atomically set a bit in memory
clear_bit  Clears a bit in memory
change_bit  Toggle a bit in memory
test_and_set_bit  Set a bit and return its old value
test_and_clear_bit  Clear a bit and return its old value
test_and_change_bit  Change a bit and return its old value
test_bit  Determine whether a bit is set

Lưu ý: Ở đây toàn bộ hoạt động xảy ra trong một bước duy nhất. Vì vậy, tất cả những thứ này được đảm bảo là nguyên tử ngay cả trên máy tính SMP và rất hữu ích để giữ sự gắn kết giữa các bộ xử lý.


9

Visual C 2010, và có lẽ nhiều trình biên dịch khác, có hỗ trợ trực tiếp cho các hoạt động boolean được tích hợp. Một bit có hai giá trị có thể, giống như boolean, vì vậy chúng ta có thể sử dụng booleans thay thế - ngay cả khi chúng chiếm nhiều không gian hơn một bit trong bộ nhớ trong đại diện này. Điều này hoạt động, thậm chí các sizeof()nhà điều hành hoạt động đúng.

bool    IsGph[256], IsNotGph[256];

//  Initialize boolean array to detect printable characters
for(i=0; i<sizeof(IsGph); i++)  {
    IsGph[i] = isgraph((unsigned char)i);
}

Vì vậy, đối với câu hỏi của bạn IsGph[i] =1, hoặc IsGph[i] =0làm cho thiết lập và xóa bool dễ dàng.

Để tìm các ký tự không thể in:

//  Initialize boolean array to detect UN-printable characters, 
//  then call function to toggle required bits true, while initializing a 2nd
//  boolean array as the complement of the 1st.
for(i=0; i<sizeof(IsGph); i++)  {
    if(IsGph[i])    {
         IsNotGph[i] = 0;
    }   else   {
         IsNotGph[i] = 1;
    }
}

Lưu ý không có gì "đặc biệt" về mã này. Nó xử lý một chút giống như một số nguyên - về mặt kỹ thuật, nó là. Một số nguyên 1 bit có thể chứa 2 giá trị và chỉ 2 giá trị.

Tôi đã từng sử dụng phương pháp này để tìm các hồ sơ cho vay trùng lặp, trong đó loan_number là khóa ISAM, sử dụng số cho vay gồm 6 chữ số làm chỉ mục vào mảng bit. Rất nhanh, và sau 8 tháng, đã chứng minh rằng hệ thống máy tính lớn mà chúng tôi nhận được dữ liệu trên thực tế đã gặp trục trặc. Sự đơn giản của mảng bit làm cho độ tin cậy về tính chính xác của chúng rất cao - ví dụ so với phương pháp tìm kiếm.


std :: bitset thực sự được triển khai dưới dạng bit bởi hầu hết các trình biên dịch
galinette

@galinette, Đồng ý. Tệp tiêu đề #include <bitset> là một tài nguyên tốt trong vấn đề này. Ngoài ra, vectơ lớp đặc biệt <bool> khi bạn cần kích thước của vectơ để thay đổi. C ++ STL, Phiên bản 2, Nicolai M. Josuttis bao quát chúng một cách triệt để trên pss 650 và 281 tương ứng. C ++ 11 bổ sung một vài khả năng mới cho std :: bitset, điều tôi đặc biệt quan tâm là hàm băm trong các thùng chứa không có thứ tự. Cảm ơn cho những người đứng đầu lên! Tôi sẽ xóa bình luận bị chuột rút của tôi. Đã đủ rác trên web. Tôi không muốn thêm vào nó.

3
Điều này sử dụng ít nhất một byte lưu trữ cho mỗi bool. Thậm chí có thể 4 byte cho các thiết lập C89 sử dụng intđể thực hiệnbool
MM

@MattMcNabb, bạn đúng rồi. Trong C ++, kích thước của kiểu int cần thiết để thực hiện boolean không được chỉ định bởi tiêu chuẩn. Tôi nhận ra câu trả lời này đã bị lỗi một thời gian trước đây, nhưng đã quyết định để nó ở đây vì mọi người dường như thấy nó hữu ích. Đối với những người muốn sử dụng bit, bình luận của galinette là hữu ích nhất vì thư viện bit của tôi ở đây ... stackoverflow.com/a/16534995/1899861

2
@RocketRoy: Có lẽ đáng để thay đổi câu tuyên bố đây là một ví dụ về "hoạt động bit", sau đó.
Ben Voigt

6

Sử dụng một trong các toán tử như được định nghĩa ở đây .

Để thiết lập một bit, được sử dụng int x = x | 0x?;ở đâu ?là vị trí bit ở dạng nhị phân.


2
0xlà tiền tố cho một chữ theo hệ thập lục phân, không phải nhị phân.
Ben Voigt

5

Dưới đây là một số macro tôi sử dụng:

SET_FLAG(Status, Flag)            ((Status) |= (Flag))
CLEAR_FLAG(Status, Flag)          ((Status) &= ~(Flag))
INVALID_FLAGS(ulFlags, ulAllowed) ((ulFlags) & ~(ulAllowed))
TEST_FLAGS(t,ulMask, ulBit)       (((t)&(ulMask)) == (ulBit))
IS_FLAG_SET(t,ulMask)             TEST_FLAGS(t,ulMask,ulMask)
IS_FLAG_CLEAR(t,ulMask)           TEST_FLAGS(t,ulMask,0)

5

Biến được sử dụng

int value, pos;

giá trị - Vị
trí dữ liệu - vị trí của bit mà chúng ta quan tâm để đặt, xóa hoặc chuyển đổi.

Đặt một chút:

value = value | 1 << pos;

Xóa một chút:

value = value & ~(1 << pos); 

Chuyển đổi một chút:

value = value ^ 1 << pos;

5
int set_nth_bit(int num, int n){    
    return (num | 1 << n);
}

int clear_nth_bit(int num, int n){    
    return (num & ~( 1 << n));
}

int toggle_nth_bit(int num, int n){    
    return num ^ (1 << n);
}

int check_nth_bit(int num, int n){    
    return num & (1 << n);
}

Trả về loại check_nth_bitcó thể bool.
Xeverous

@Xeverous có, nó phụ thuộc vào ý định của người gọi
Sazzad Hissain Khan

5

Giả sử vài thứ đầu tiên
num = 55 Integer thực hiện các thao tác bitwise (đặt, nhận, xóa, chuyển đổi).
n = 40 dựa trên vị trí bit để thực hiện các hoạt động bitwise.

Làm thế nào để có được một chút?

  1. Để có được nthbit của num đúng num, nlần. Sau đó thực hiện bitwise AND &với 1.
bit = (num >> n) & 1;

Làm thế nào nó hoạt động?

       0011 0111 (55 in decimal)
    >>         4 (right shift 4 times)
-----------------
       0000 0011
     & 0000 0001 (1 in decimal)
-----------------
    => 0000 0001 (final result)

Làm thế nào để thiết lập một chút?

  1. Để thiết lập một số cụ thể của số. Ca trái 1 nlần. Sau đó thực hiện thao tác bitwise HOẶC |với num.
num |= (1 << n);    // Equivalent to; num = (1 << n) | num;

Làm thế nào nó hoạt động?

       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
       0001 0000
     | 0011 0111 (55 in decimal)
-----------------
    => 0001 0000 (final result)

Làm thế nào để xóa một chút?

  1. Ca trái 1, nlần tức là 1 << n.
  2. Thực hiện bổ sung bitwise với kết quả trên. Vì vậy, bit thứ n trở nên không được đặt và phần còn lại của bit được đặt tức là~ (1 << n) .
  3. Cuối cùng, thực hiện thao tác bitwise AND &với kết quả trên và num. Ba bước trên có thể được viết là num & (~ (1 << n));

Các bước để xóa một chút

num &= (~(1 << n));    // Equivalent to; num = num & (~(1 << n));

Làm thế nào nó hoạt động?

       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
     ~ 0001 0000
-----------------
       1110 1111
     & 0011 0111 (55 in decimal)
-----------------
    => 0010 0111 (final result)

Làm thế nào để chuyển đổi một chút?

Để chuyển đổi một chút, chúng tôi sử dụng toán ^tử XOR bitwise . Toán tử Bitwise XOR ước tính thành 1 nếu bit tương ứng của cả hai toán hạng khác nhau, nếu không thì ước tính là 0.

Có nghĩa là để chuyển đổi một chút, chúng ta cần thực hiện thao tác XOR với bit bạn muốn chuyển đổi và 1.

num ^= (1 << n);    // Equivalent to; num = num ^ (1 << n);

Làm thế nào nó hoạt động?

  • Nếu bit để chuyển đổi là 0 thì 0 ^ 1 => 1.
  • Nếu bit để chuyển đổi là 1 thì 1 ^ 1 => 0.
       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
       0001 0000
     ^ 0011 0111 (55 in decimal)
-----------------
    => 0010 0111 (final result)

Đề nghị đọc - Bài tập toán tử bitwise


Cảm ơn vì lời giải thích chi tiết. Đây là liên kết cho vấn đề thực hành cho liên kết
Chandra Shekhar

4

Làm thế nào để bạn thiết lập, xóa và chuyển đổi một bit?

Để giải quyết một cạm bẫy mã hóa phổ biến khi cố gắng tạo mặt nạ:
1không phải lúc nào cũng đủ rộng

Vấn đề gì xảy ra khi numbermột loại rộng hơn 1?
xcó thể quá tuyệt vời cho sự thay đổi 1 << xdẫn đến hành vi không xác định (UB). Ngay cả khi xkhông quá lớn, ~có thể không lật đủ các bit quan trọng nhất.

// assume 32 bit int/unsigned
unsigned long long number = foo();

unsigned x = 40; 
number |= (1 << x);  // UB
number ^= (1 << x);  // UB
number &= ~(1 << x); // UB

x = 10;
number &= ~(1 << x); // Wrong mask, not wide enough

Để đảm bảo 1 đủ rộng:

Mã có thể sử dụng 1ullhoặc theo phương pháp sư phạm (uintmax_t)1và để trình biên dịch tối ưu hóa.

number |= (1ull << x);
number |= ((uintmax_t)1 << x);

Hoặc diễn viên - điều này làm cho các vấn đề về mã hóa / đánh giá / bảo trì giữ cho diễn viên chính xác và cập nhật.

number |= (type_of_number)1 << x;

Hoặc nhẹ nhàng thúc đẩy 1bằng cách buộc một phép toán có độ rộng tối thiểu bằng loại number.

number |= (number*0 + 1) << x;

Như với hầu hết các thao tác bit, tốt nhất để làm việc với unsigned loại chứ không phải ký kết những


Cái nhìn thú vị về một câu hỏi cũ! Không number |= (type_of_number)1 << x;phải cũng không number |= (number*0 + 1) << x;thích hợp để đặt bit dấu của loại đã ký ... Như một vấn đề thực tế, cũng không phải number |= (1ull << x);. Có một cách di động để làm điều đó theo vị trí?
chqrlie

1
@chqrlie IMO, cách tốt nhất để tránh thiết lập bit dấu và mạo hiểm UB hoặc IDB với ca là sử dụng các loại không dấu . Mã dịch chuyển cao di động quá phức tạp để được chấp nhận.
chux - Phục hồi Monica

3

Phiên bản templated C ++ 11 (đặt trong tiêu đề):

namespace bit {
    template <typename T1, typename T2> inline void set  (T1 &variable, T2 bit) {variable |=  ((T1)1 << bit);}
    template <typename T1, typename T2> inline void clear(T1 &variable, T2 bit) {variable &= ~((T1)1 << bit);}
    template <typename T1, typename T2> inline void flip (T1 &variable, T2 bit) {variable ^=  ((T1)1 << bit);}
    template <typename T1, typename T2> inline bool test (T1 &variable, T2 bit) {return variable & ((T1)1 << bit);}
}

namespace bitmask {
    template <typename T1, typename T2> inline void set  (T1 &variable, T2 bits) {variable |= bits;}
    template <typename T1, typename T2> inline void clear(T1 &variable, T2 bits) {variable &= ~bits;}
    template <typename T1, typename T2> inline void flip (T1 &variable, T2 bits) {variable ^= bits;}
    template <typename T1, typename T2> inline bool test_all(T1 &variable, T2 bits) {return ((variable & bits) == bits);}
    template <typename T1, typename T2> inline bool test_any(T1 &variable, T2 bits) {return variable & bits;}
}

Mã này bị hỏng. (Ngoài ra, tại sao bạn có ;sau định nghĩa chức năng của mình?)
melpomene

@melpomene Mã không bị hỏng, tôi đã kiểm tra nó. Bạn có nghĩa là nó sẽ không biên dịch hoặc kết quả là sai? Về phần thêm ';' Tôi không nhớ, những cái đó thực sự có thể được gỡ bỏ.
Joakim L. Christiansen

(variable & bits == bits)?
melpomene

Cảm ơn bạn đã chú ý, nó được cho là((variable & bits) == bits)
Joakim L. Christiansen

sử dụng std::bitsettrong c ++ 11
pqnet

0

Chương trình này dựa trên giải pháp trên của @ Jeremy. Nếu ai đó muốn nhanh chóng chơi xung quanh.

public class BitwiseOperations {

    public static void main(String args[]) {

        setABit(0, 4); // set the 4th bit, 0000 -> 1000 [8]
        clearABit(16, 5); // clear the 5th bit, 10000 -> 00000 [0]
        toggleABit(8, 4); // toggle the 4th bit, 1000 -> 0000 [0]
        checkABit(8,4); // check the 4th bit 1000 -> true 
    }

    public static void setABit(int input, int n) {
        input = input | ( 1 << n-1);
        System.out.println(input);
    }


    public static void clearABit(int input, int n) {
        input = input & ~(1 << n-1);
        System.out.println(input);
    }

    public static void toggleABit(int input, int n) {
        input = input ^ (1 << n-1);
        System.out.println(input);
    }

    public static void checkABit(int input, int n) {
        boolean isSet = ((input >> n-1) & 1) == 1; 
        System.out.println(isSet);
    }
}


Output :
8
0
0
true

-2

Hãy thử một trong các chức năng này trong ngôn ngữ C để thay đổi n bit:

char bitfield;

// Start at 0th position

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}

Hoặc là

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}

Hoặc là

void chang_n_bit(int n, int value)
{
    if(value)
        bitfield |= 1 << n;
    else
        bitfield &= ~0 ^ (1 << n);
}

char get_n_bit(int n)
{
    return (bitfield & (1 << n)) ? 1 : 0;
}

value << ncó thể gây ra hành vi không xác định
MM
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.