2009-10-10 5 views
5

Có cách nào di động trong C để tìm ra mặt nạ cho một trường bit tại thời gian biên dịch không?Mặt nạ bitfield trong C

Lý tưởng nhất, tôi muốn để có thể nguyên tử rõ ràng một lĩnh vực như thế này:

struct Reference { 
    unsigned age : 3; 
    unsigned marked : 1; 
    unsigned references : 4; 
}; 

struct Reference myRef; 
__sync_and_and_fetch(&myRef, age, ~AGE_MASK); 

Nếu không tôi phải đưa ra một khóa trên các cấu trúc, mà là nhiều hơn nặng hơn tôi muốn.

+1

__sync_and_and_fetch không hoạt động trên bitfields: "GCC sẽ cho phép bất kỳ loại vô hướng hoặc kiểu con trỏ nào có chiều dài 1, 2, 4 hoặc 8 byte". – sambowry

Trả lời

2

Hoặc, nếu bạn thực sự muốn mặt nạ:

union Reference { 
    unsigned asWord; 
    struct { 
    unsigned age : 3; 
    unsigned marked : 1; 
    unsigned references : 4; 
    } asFields; 
} 

Reference agemask_ref; 
agemask_ref.asFields = (typeof(agemask_ref.asFields)){0, -1, -1}; 
unsigned agemask = agemask_ref.asWord; 
+2

Lưu ý rằng 'typeof()' là một phần mở rộng GCC và không thể di chuyển sang các trình biên dịch khác. –

+0

Cảm ơn bạn Keith - đẹp hack. – Grandpa

+0

Trong sự tò mò, tại sao điều này được chấp nhận nếu nó không mang tính di động? Chỉ tò mò thôi. – BobbyShaftoe

1

Tôi không nghĩ là có thể - ngay cả với offsetof() mà làm việc cho bù đắp byte nhưng dường như không làm việc cho bitfields. Tôi sẽ redeclare các lĩnh vực như enums/định nghĩa (0x01 0x02 vv) và quản lý các bit chính mình, vì vậy bạn có thể nhận được thay đổi nguyên tử của bạn.

2

Bạn có thể làm một cái gì đó như:

union Reference { 
    unsigned asWord; 
    struct { 
    unsigned age : 3; 
    unsigned marked : 1; 
    unsigned references : 4; 
    } asFields; 
} 

Để nguyên tử rõ ràng một lĩnh vực myRef, làm

(+ đang unshown để xử lý khi compare_and_swap thất bại)

2

I don' t biết làm thế nào để làm điều đó tại thời gian biên dịch, nhưng tại thời gian chạy nó phải là một vấn đề đơn giản của union'ing một thể hiện của cấu trúc bitfield của bạn với một int unsigned có kích thước thích hợp, và thiết lập tất cả các lĩnh vực 0 ex thu thập một trong những bạn quan tâm mà nên được thiết lập để tất cả 1s - giá trị của int unsigned là sau đó bitmask bạn muốn. Bạn có thể làm điều đó cho mỗi lĩnh vực lúc khởi động, có thể với một macro để tránh một số mã lặp đi lặp lại. Có thể không đủ?

+0

+1 Tôi có thể tưởng tượng điều này hoạt động. Có thể có vấn đề lông với endianness và bitfields, nhưng tôi có thể tưởng tượng điều này là an toàn. Nhưng tôi không phải là một guru tiêu chuẩn. –

0

Vâng, điều này có thể được thực hiện. Bạn cần nắm bắt giá trị và thực hiện thao tác. Sau đó, bạn cần phải sử dụng một so sánh nguyên tử và trao đổi (như InterlockedCompareExchange trên Windows) để lưu trữ giá trị mới nếu bộ nhớ vẫn chứa giá trị cũ. Nếu ai đó sửa đổi giá trị, bạn lặp lại và thử lại. Lưu ý đây là một mẫu tiêu chuẩn để thực hiện bất kỳ thao tác nào trên một đoạn dữ liệu có kích thước từ mà tại đó không có sẵn nội tại.

Đoạn mã dưới đây sử dụng int - như Keith đã chỉ ra rằng bạn có thể sử dụng một liên kết để có thể nhận được các giá trị của cấu trúc dưới dạng int.

int oldValue, newValue; 
do 
{ 
    oldValue = myRef; 
    newValue = oldValue & ~AGE_MASK; 
} while (InterlockedCompareExchange(&myRef, newValue, oldValue) != oldValue);