struct Type {
uint8_t var : 3;
};
int main()
{
struct Type bar;
bar.var = 1;
uint8_t baz = bar.var << 5;
}
According to the standard, left shifting more than the width of the left operand type is undefined behavior:
6.5.7 Bitwise shift operators/3 The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.
But what about bit fields? Isn't it at least eight bits here?
CodePudding user response:
There will be references to the integer promotion of the left operand. The following is the relevant promotion:
6.3.1.1.2 [...] If an
int
can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to anint
; [...]
The promoted left operand is an int
.
About shifting, the spec says
6.5.7.3 The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.
The width of the promoted left operand — the width of an int
— is at least 16. 5 is much less than 16.
No undefined behaviour yet.
The spec goes on:
6.5.7.4 The result of
E1 << E2
isE1
left-shiftedE2
bit positions; vacated bits are filled with zeros. IfE1
has an unsigned type, the value of the result is E1 × 2E2, reduced modulo one more than the maximum value representable in the result type. IfE1
has a signed type and nonnegative value, and E1 × 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.
The LHS has an unsigned type.
No undefined behaviour yet.
Finally, we have the assignment.
6.5.16.1.2 In simple assignment (
=
), the value of the right operand is converted to the type of the assignment expression and replaces the value stored in the object designated by the left operand.
6.3.1.3.2 Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.60)
No undefined behaviour there either.
CodePudding user response:
According to the C11 standard section 6.7.2.1.5:
"A bit-field shall have a type that is a qualified or unqualified version of _Bool, signed int, unsigned int, or some other implementation-defined type. It is implementation-defined whether atomic types are permitted."
This means that despite var
being defined as only 3-bits wide in struct Type
its type is still uint8_t
, so when it is used in the expression bar.var << 5
, integer promotion rules apply as would be expected for its underlying type.
This means that the value of bar.var
is implicitly promoted to int
in accordance with integer promotion rules for integer-type values that can be represented by type int
, the shift is performed in a minimum 16-bit space, then the result is implicitly demoted back to uint8_t
and stored in baz
, so this operation is perfectly defined by the standard.