Câu hỏi Làm cách nào để bạn đặt, xóa và chuyển đổi một bit?


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


2053
2017-09-07 00:42


gốc


đọc này: graphics.stanford.edu/~seander/bithacks.html và, khi bạn sẽ nắm vững điều này, hãy đọc phần này: realtimecollisiondetection.net/blog/?p=78 - ugasoft
Bạn cũng có thể quan tâm đến việc thanh toán Bit Twiddler, Bit Twiddling Hacksvà Thuật toán ma thuật tổng hợp.
Liên kết này đã giúp tôi hiểu rằng các hoạt động này thực sự hoạt động như thế nào - cs.umd.edu/class/sum2003/cmsc311/Notes/BitOp/setBitI.html Ở đây bạn có thể tìm thấy nhiều hoạt động thú vị hơn - cs.umd.edu/class/sum2003/cmsc311/Notes - rajya vardhan
Tôi có thể dễ dàng tưởng tượng điều này không phải cho câu hỏi chính nó nhưng để sinh ra một hướng dẫn tham khảo rất hữu ích. Xem xét tôi đã ở đây khi tôi cần một chút thông tin, anyway. - Joey van Hummel
@glglgl / Jonathon nó có đủ ý nghĩa cho C ++ để được gắn thẻ như vậy. Đó là một câu hỏi lịch sử với rất nhiều lưu lượng truy cập và thẻ C ++ sẽ giúp các lập trình viên quan tâm tìm thấy nó thông qua tìm kiếm google. - Luchian Grigore


Các câu trả lời:


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

Sử dụng toán tử OR bitwise (|) để thiết lập một chút.

number |= 1UL << n;

Điều đó sẽ thiết lập nthứ bit number.

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

Xóa 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 nthứ bit number. Bạn phải đảo ngược chuỗi bit với toán tử NOT bitwise (~), sau đó VÀ nó.

Toggling 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ẽ chuyển đổi nthứ bit 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, hãy chuyển số n sang phải, sau đó bitwise AND:

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

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

Thay đổi nth bit để x

Đặt nth bit hoặc 1 hoặc là 0 có thể đạt được với những điều sau đây về việc bổ sung C ++ bổ sung của 2:

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

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

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

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ó thường là một ý tưởng tốt để sử dụng các loại unsigned cho thao tác bit di động.

Nó cũng thường là một ý tưởng tốt để 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ư cộng đồng wiki trả lời thêm) hoặc một số loại đóng gói.


3003
2017-09-07 00:50



Tôi muốn lưu ý rằng trên các nền tảng có hỗ trợ gốc cho bit set / clear (ex, AVR vi điều khiển), trình biên dịch sẽ thường dịch 'myByte | = (1 << x)' thành bit gốc thiết lập / hướng dẫn rõ ràng bất cứ khi nào x là một hằng số, ví dụ: (1 << 5), hoặc const unsigned x = 5. - Aaron
bit = number & (1 << x); sẽ không đặt giá trị của bit x vào bit trừ khi bit có loại _Bool (<stdbool.h>). Nếu không, bit = !! (số & (1 << x)); sẽ.. - Chris
tại sao bạn không thay đổi người cuối cùng thành bit = (number >> x) & 1 - aaronman
1 là một int theo nghĩa đen, được ký. Vì vậy, tất cả các hoạt động ở đây hoạt động trên các con 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 bổ sung hai hoặc chuyển số học vì vậy tốt hơn nên sử dụng 1U. - Siyuan Ren
tôi thích number = number & ~(1 << n) | (x << n); để thay đổi bit thứ n thành x. - Eliko


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

Hoặc là Tăng cường phiên bản: boost::dynamic_bitset.

Không cần phải cuộn của riêng bạ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 thư viện chuẩn bitet có kích thước thời gian biên dịch.


383
2017-09-18 00:34



+1. Không phải là std :: bitset là có thể sử dụng từ "C", nhưng như tác giả gắn thẻ câu hỏi của mình với "C + +", AFAIK, câu trả lời của bạn là tốt nhất xung quanh ở đâ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
@andrewdotnich: vector <bool> là (không may) 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
Có lẽ không ai đề cập đến nó vì điều này đã được gắn thẻ. 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ó thể là một loài chim rất hiếm gặp trong số các trình biên dịch được nhúng nhất. - Lundin
@ Martin Nó rất đúng. Bên cạnh những kẻ giết người thực hiện 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ộ các thư viện chuẩn hoàn toàn, vì chúng là một nỗi đau để xác minh. Hầu hết các nhánh nhúng đều chấp nhận các tiêu chuẩn như MISRA, đòi hỏi 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ụ như vậy, không chỉ các folks được nhúng). Nói chung mọi người có những việc tốt hơn để làm hơn là chạy phân tích tĩnh thông qua toàn bộ thư viện 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
@ Lundin: báo cáo của bạn là quá rộng (do đó vô dụng để tranh luận về). Tôi chắc chắn rằng tôi có thể tìm thấy các tình huống là chúng đúng. Đ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 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ả các hệ thống sử dụng chúng, chúng đượ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 chính xác (vì có hệ thống chúng được sử dụng). - Martin York


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 một trường 3 bit (thực ra, nó là ba bit 1 bit). Các thao tác bit bây giờ trở nên đơn giản hơn một chút (haha):

Để đặt 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 có kích thước cố định. Nếu không, bạn phải sử dụng các kỹ thuật bit-twiddling được mô tả trong các bài viết trước.


213
2017-09-11 00:56



Tôi đã luôn luôn tìm thấy bằng cách sử dụng bitfields là một ý tưởng tồi. Bạn không có quyền kiểm soát thứ tự các bit được cấp phát (từ trên cùng hoặc dưới cùng), điều này làm cho nó không thể tuần tự hóa giá trị theo cách ổn định / di động ngoại trừ bit-at-a-time. Cũng không thể kết hợp số học bit DIY với bitfield, ví dụ như tạo mặt nạ để kiểm tra một vài bit cùng một lúc. Bạn có thể sử dụng && và hy vọng trình biên dịch sẽ tối ưu hóa nó một cách chính xác ... - R..
Các trường bit là xấu 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ư đã làm điều đó cho một chương trình lĩnh vực bit cần sự tuân thủ MISRA-C. MISRA-C thi hành tất cả các hành vi được xác định thực hiện để được ghi lại, vì vậy tôi đã viết khá một bài luận về mọi thứ có thể đi sai trong các trường bit. Thứ tự bit, endianess, bit đệm, byte đệm, các vấn đề liên kết khác nhau, chuyển đổi loại tiềm ẩn và rõ ràng đến và từ một trường bit, UB nếu int không được sử dụng và vân vân. Thay vào đó, hãy sử dụng các toán tử bitwise cho ít lỗi và mã di động hơn. Các trường bit là hoàn toàn dư thừa. - Lundin
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 số giá trị nhỏ vào một int đơn, 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ế chứa int, bạn chỉ cần hỏi về sự cố. - Ferruccio
@endolith: Đó sẽ không phải là một ý tưởng hay. Bạn có thể làm cho nó hoạt động, nhưng nó không nhất thiết phải di chuyển đến một bộ xử lý khác, hoặc tới một trình biên dịch khác hoặc thậm chí là bản phát hành tiếp theo của trình biên dịch tương tự. - Ferruccio
@R. Có thể sử dụng cả hai, struct có thể được đặt bên trong một (thường là ẩn danh) union với một số nguyên vv Nó hoạt động. (Tôi nhận ra đây là một btw thread cũ) - Shade


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

/* 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)))

/* 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))

125
2017-09-08 21:07



Uh Tôi nhận ra đây là bài đăng cũ 5 năm nhưng không có sự trùng lặp đối số trong bất kỳ macro nào, Dan - Robert Kelly
BITMASK_CHECK(x,y) ((x) & (y)) cần phải ((x) & (y)) == (y) nếu không nó sẽ trả về kết quả không chính xác trên mặt nạ multibit (ví dụ: 5 so với 3) / * Xin chào tất cả các gravediggers:) * / - brigadir
1 nên là (uintmax_t)1 hoặ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 long hoặc loại lớn hơn - M.M
Hoặc là 1ULL làm việc cũng như (uintmax_t) trên hầu hết các triển khai. - Peter Cordes
@brigadir: phụ thuộc vào việc bạn có muốn kiểm tra bất kỳ bit nào được đặt hay tất cả các bit được đặt hay không. Tôi đã cập nhật câu trả lời để đưa vào cả hai tên mô tả. - Peter Cordes


Nó đôi khi đáng để sử dụng enum đến Tên 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. I E. viết

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

để thiết lập, rõ ràng và thử nghiệm. Bằng cách này, bạn ẩn số ma thuật khỏi phần còn lại của mã.

Khác với điều đó tôi xác nhận giải pháp của Jeremy.


99
2017-09-17 02:04



Cách khác bạn có thể làm clearbits() thay vì &= ~. Tại sao bạn sử dụng một enum cho điều này? Tôi nghĩ rằng đó là để tạo ra một loạt các biến độc đáo với giá trị tùy ý ẩn, nhưng bạn đang gán một giá trị xác định cho mỗi giá trị. Vậy lợi ích gì so với việc chỉ định chúng là biến? - endolith
@endolith: Việc sử dụng enums cho các tập hợp các hằng số liên quan quay 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 const short hoặc bất cứ thứ gì là chúng được nhóm lại với nhau một cách rõ ràng. Và khi bạn muốn chúng cho một cái gì đó khác so với bitmasks bạn nhận được đánh số tự động. Trong c + + tất nhiên, chúng cũng tạo thành các kiểu riêng biệt, cho phép bạn kiểm tra lỗi tĩnh một chút. - dmckee
Bạn sẽ nhận được vào hằng số enum không xác định nếu bạn không xác định một hằng số cho mỗi giá trị có thể có của các bit. Cái gì enum ThingFlags giá trị cho ThingError|ThingFlag1, ví dụ? - Luis Colorado
Nếu bạn sử dụng phương pháp này, xin lưu ý rằng hằng số enum luôn là loại đã ký int. Điều này có thể gây ra tất cả các cách lỗi tinh vi vì quảng cáo số nguyên ẩn hoặc hoạt động bitwise trên các loại đã ký. thingstate = ThingFlag1 >> 1 ví dụ sẽ gọi hành vi được xác định thực hiện. thingstate = (ThingFlag1 >> x) << y có thể gọi hành vi không xác định. Và cứ thế. Để an toàn, luôn chuyển sang loại không dấu. - Lundin
@ 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


Từ snip-c.zip's bitops.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 chung mà bạn dường như gặp vấn đề với tất cả những điều này là "(1L << (posn))". Tất cả điều này là tạo ra một mặt nạ với một chút trên và 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ẽ đánh giá:

    0000 0000 0000 0000 0000 0000 0000 0001 binary.

Nếu posn == 8, nó sẽ đánh giá

    0000 0000 0000 0000 0000 0001 0000 0000 binary.

Nói cách khác, nó chỉ đơn giản là tạo ra một trường 0 với 1 ở mức xác định Chức vụ. Phần khó khăn duy nhất là trong macro BitClr (), nơi chúng ta cần thiết lập một bit 0 trong một trường của 1. Điều này được thực hiện bằng cách sử dụng số 1 bổ sung cùng một biểu thức đượ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ố giống như bạn đề xuất, bằng cách sử dụng các toán tử bitwise và (&) hoặc (|) và xor (^). Kể từ khi mặt nạ có kiểu dài, các macro sẽ hoạt động tốt trên char, short's, int's, hoặc dài.

Điểm mấu chốt là đây là giải pháp chung cho toàn bộ lớp các vấn đề. Đó là, 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 một, nhưng tại sao làm điều đó? Hãy nhớ rằng, sự thay thế vĩ mô xảy ra trong tiền xử lý và vì vậy mã được tạo ra sẽ phản ánh thực tế là các giá trị được coi là không đổi bởi trình biên dịch - tức là nó chỉ hiệu quả để sử dụng các macro tổng quát là "phát minh lại bánh xe" mỗi lần bạn cần làm thao tác bit.

Không thuyết phục? Dưới đây là một số mã thử nghiệm - Tôi đã sử dụng Watcom C với tối ưu hóa đầy đủ và không sử dụng _cdecl để việc tháo gỡ kết quả sẽ sạch sẽ như khả thi:

---- [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

---- [finis] ------------------------------------------- ----------------------


34
2018-06-05 14:18



2 điều về điều này: (1) trong perusing macro của bạn, một số có thể không chính xác tin rằng các macro thực sự thiết lập / rõ ràng / flip bit trong arg, tuy nhiên không có nhiệm vụ; (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, bạn sẽ tìm thấy một vấn đề (đọc tập thể dục) - Dan
-1 Đây chỉ là sự bối rối kỳ quặc. 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, nó là rất thực hành xấu. Sau đó, một số điểm kỳ quặc: đầu tiên, 1L được ký, nghĩa là tất cả các thao tác 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 dưới dạng ký dài. Không tốt. Thứ hai, điều này sẽ làm việc 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ể ở cấp int. Thứ ba, các macro giống như chức năng là gốc rễ của mọi điều ác: bạn không có an toàn kiểu nào cả. Ngoài ra, nhận xét trước về việc chuyển nhượng không có giá trị. - Lundin
Điều này sẽ thất bại nếu arg Là long long. 1L cầ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) - M.M
Bạn đã tối ưu hóa cho kích thước mã? Trên các CPU chủ đạo 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ó viết các thành phần 8-bit của EAX. (Nó tốt trên CPU AMD, hoặc những người khác không đổi tên một phần sổ đăng ký riêng biệt với thanh ghi đầy đủ. Haswell / Skylake không đổi tên AL riêng biệt, nhưng họ đổi tên AH.). - Peter Cordes


Đố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 & toán tử được sử dụng kiểm tra 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ử: thiết lập bit

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

29
2017-09-07 00:45





Sử dụng 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 XXXb cho rõ ràng. Có thể bạn sẽ làm việc với biểu diễn HEX, tùy thuộc vào cấu trúc dữ liệu mà bạn đang đóng gói bit.


26
2017-07-13 06:53



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


Đâ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 char lên đến 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)))))

Để đặt bit:

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.v.


24
2018-06-14 15:23



Thật tốt để đọc nhưng người ta phải 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 nhiều khả năng sẽ không làm những gì người gọi muốn. - foraidt
Thật. =) Một biến thể bạn có thể muốn là tách nó thành 2 macro, 1 để giải quyết phần tử mảng và biến thể khác để chuyển bit thành vị trí, ala BITCELL(a,b) |= BITMASK(a,b); (cả hai đều a như một đối số để xác định kích thước, nhưng sau này sẽ không bao giờ đánh giá a vì nó chỉ xuất hiện trong sizeof). - R..
@R .. Câu trả lời này thực sự là cũ, nhưng tôi có lẽ sẽ thích một chức năng cho một vĩ mô trong trường hợp này. - PC Luddite
Nhỏ: thứ 3 (size_t) diễn viên dường như chỉ ở đó để đảm bảo một số toán học chưa ký với %. Có thể (unsigned) đó. - chux
Các (size_t)(b)/(8*sizeof *(a)) không cần thiết có thể thu hẹp b trước khi 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


Vì điều này được gắn thẻ "nhúng" tôi sẽ giả sử 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 đổi-viết, đoàn thể, cấu trúc, vv).

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

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


22
2017-11-06 11:30



Điều cần biết có thể có các lựa chọn nhanh hơn. Vi mô là gì? - JeffV
Micro là Coldfire MCF52259, sử dụng C trong Codewarrior. Nhìn vào disassembler / 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 các lệnh CPU-hogging khác trong các vòng thời gian quan trọng - hạn chế biến bằng cách thực hiện var% = max_val chi phí một đống chu kỳ CPU mỗi lần, trong khi thực hiện nếu (var> max_val) var- = max_val chỉ sử dụng một vài hướng dẫn. <br> Một hướng dẫn tốt cho một vài thủ thuật khác là ở đây: codeproject.com/Articles/6154/… - John U
Quan trọng hơn, các thanh ghi I / O được ánh xạ bộ nhớ trợ giúp cung cấp một cơ chế cho các bản cập nhật nguyên tử. Đọc / sửa đổi / ghi có thể đi rất nặng nếu trình tự bị gián đoạn. - Ben Voigt
Hãy nhớ rằng tất cả các thanh ghi cổng sẽ được định nghĩa là volatile và 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 như vậy. Vì vậy, thực hành tốt là phải tháo rời mã như vậy và xem nó hoạt động như thế nào ở cấp độ lắp ráp. - Lundin