Bit-field declared in a struct or union
ID |
objectivec.maintainability.avoid_bit_fields |
Severity |
info |
Remediation Complexity |
medium |
Remediation Risk |
medium |
Remediation Effort |
medium |
Resource |
Type Safety |
Language |
Objective-C |
Description
Reports a bit-field declaration - a member declared with a width, as in unsigned int enabled : 1 - including the anonymous padding form unsigned int : 4. A signed bit-field of width one is not reported here; the signed-bit-field rule owns that case, so the two rules never report the same member. A case or default label, a keyword argument in a message send, a ternary, and occurrences inside a block comment are not reported.
Rationale
Almost everything about a bit-field is left to the implementation: whether the bits are allocated from the low or the high end of the storage unit, whether a field may straddle a unit boundary, how much padding the compiler inserts, and - for a plain int field - whether the field is even signed. That makes the memory layout unportable, so a struct of bit-fields cannot be relied on to describe a hardware register or a wire format, which is usually why it was written. A bit-field also has no address, so it cannot be passed by pointer, and concurrent writes to two different fields of the same storage unit are a data race even though the fields are distinct.
The following code illustrates the pattern detected by this rule:
struct DeviceFlags {
// FLAGGED: Bit-field declared in a struct or union
unsigned int enabled : 1;
// FLAGGED: Bit-field declared in a struct or union
unsigned int mode : 3;
// FLAGGED: Bit-field declared in a struct or union
uint8_t retries : 4;
// FLAGGED: Bit-field declared in a struct or union
unsigned int : 4;
// FLAGGED: Bit-field declared in a struct or union
signed int span : 3;
signed int direction : 1;
int polarity : 1;
int8_t marker : 1;
NSInteger mark : 1;
// FLAGGED: Bit-field declared in a struct or union
BOOL ready : 1;
// FLAGGED: Bit-field declared in a struct or union
NSUInteger counter : 1;
BOOL plain;
uint8_t tally;
};
Remediation
Where the members are independent flags, declare them as separate BOOL members and let the compiler choose the layout. Where the packing genuinely matters, hold a single integer of a fixed-width type and define named masks and shift accessors for each field, so the layout is written in the source rather than chosen by the compiler; an NS_OPTIONS enum is the idiomatic Objective-C form of that.