Reputation: 434
I have defined a struct like:
struct mystruct {
uint32_t onebyte : 8;
uint32_t twobytes : 16;
uint32_t threebits : 3;
};
I know that C defines bit-fields to be at least as wide as specified, but the compiler may use more memory (e.g. 3*4 bytes in this case). However, the set width is a guaranteed minimum and if a value exceeds the according range, the application might still work correctly by chance.
To run some debug assertions, I want my code to check whether a value exceeds the allowed range before setting the member value:
assert(someval < (1 << sizeofbitfieldmemberinbits(((mystruct*)NULL)->threebits)));
For this specific case it might be a feasible solution to use a slightly different approach without any sizeof()
call, but I am not sure if this is guaranteed to work:
assert(someveal <= ((mystruct){.threebits = -1}).threebits);
Anyway, is there a way to determine the guaranteed minimum size of a bit-field member of a C struct in bits (or at least in bytes)?
I am looking for an expression like sizeofbitfieldmemberinbits()
that can be calculated by the compiler at build time (like ((mystruct){.threebits = -1}).threebits
can be evaluated to 0x7
).
Edit:
As John Bollinger pointed out, the memory allocated for a bit-field member can be larger than the specified number of bits, but the member can never hold a value larger than (1 << #bits) - 1
. However, when I attempt to set a value that is out of bounds, it will be truncated implicitly (at runtime). With the assertion I want to check for cases where such truncation not just might occur, but when actually does.
Upvotes: 0
Views: 1281
Reputation: 153338
is there a way to determine the guaranteed minimum size of a bit-field member of a C struct in bits
I want to check for cases where such truncation not just might occur, but when actually does.
assert()
is insufficient as its related code is not certainly present at runtime.
Check against the maximum value of the field. Easy to do when the field is some unsigned type.
#include <inttypes.h>
#include <stdio.h>
int main(void) {
struct mystruct {
uint32_t onebyte :8;
uint32_t twobytes :16;
uint32_t threebits :3;
};
struct mystruct obj;
for (int test = 0; test < 20; test++) {
unsigned n = rand() % 10;
// I want to check for cases where such truncation not just might occur,
// but when actually does.
if (n > (struct mystruct) {.threebits = -1}.threebits) {
printf("Truncation will occur, %u\n", n);
}
obj.threebits = n;
}
return obj.threebits;
}
Output
Truncation will occur, 9
Truncation will occur, 8
Truncation will occur, 9
Truncation will occur, 8
Truncation will occur, 9
Upvotes: 0
Reputation: 180058
I am looking for an expression like
sizeofbitfieldmemberinbits()
that can be calculated by the compiler at build time
The Standard's term for what you are describing is "constant expression":
A constant expression can be evaluated during translation rather than runtime, and accordingly may be used in any place that a constant may be.
(C2011, 6.6/2)
You go on to clarify the purpose for which you want to use such a constant expression:
With the assertion I want to check for cases where such truncation not just might occur, but when actually does.
Note, however, that
For that purpose, the size of the bitfield is secondary. What you actually want is the maximum representable value. For bitfields of signed types, maybe you want the minimum, too.
You don't actually need a constant expression for use in a regular assertion such as you demonstrate (as opposed to a static assertion). The expression in a regular assertion is evaluated at runtime.
On the other hand, some expressions that do not satisfy the Standard's definition of a constant expression may still be computed at translation (compile) time by some implementations.
Points (2) and (3) are fortunate for you, because bitfields have second-class types that are not directly expressible. There are no values of any bitfield type outside the context of a host structure object, and there is no type name with which the effective type of a bitfield can be expressed. And that means there is no constant expression that evaluates to the number of bits or maximum value of a bitfield member, unless it incorporates prior knowledge of that member, because structs (including struct literals) are not among the operands that may appear in a suitable constant expression:
An arithmetic constant expression shall have arithmetic type and shall only have operands that are integer constants, floating constants, enumeration constants, character constants,
sizeof
expressions whose results are integer constants, and_Alignof
expressions. Cast operators in an arithmetic constant expression shall only convert arithmetic types to arithmetic types, except as part of an operand to asizeof
or_Alignof
operator.
After all that, I think the question really boils down to this:
I am not sure if this is guaranteed to work:
assert(someveal <= ((mystruct){.threebits = -1}).threebits);
For unsigned bitfields such as your mystruct.threebits
, it's guaranteed to work in C99 or later. Earlier versions of C do not have compound literals or designated initializers, however, and some C implementations you might run into even today do not conform to C99. On such an implementation, you might instead just define a (maybe const
, maybe static
) instance of your struct in which to record the limits ...
static const struct mystruct mystruct_limits = { -1, -1, -1 };
... and then compare to its members:
assert(someveal <= mystruct_limits.threebits);
Note here that struct member initializers are subject to the same conversions that apply in simple assignment, so as long as the members have unsigned types, the -1s as initializer values are well defined to have the wanted effect.
Note also that although the const
is desirable for the purpose, it was not standardized until C99, either. It was a pretty common extension before C99, though, and you are far less likely to run into a C compiler that rejects const
than one that rejects compound literals.
Upvotes: 2