I have come across a strange behavior on signed bit-fields:
#include <stdio.h>
struct S {
long long a31 : 31;
long long a32 : 32;
long long a33 : 33;
long long : 0;
unsigned long long b31 : 31;
unsigned long long b32 : 32;
unsigned long long b33 : 33;
};
long long f31(struct S *p) { return p->a31 + p->b31; }
long long f32(struct S *p) { return p->a32 + p->b32; }
long long f33(struct S *p) { return p->a33 + p->b33; }
int main() {
struct S s = { -2, -2, -2, 1, 1, 1 };
long long a32 = -2;
unsigned long long b32 = 1;
printf("f31(&s) => %lld\n", f31(&s));
printf("f32(&s) => %lld\n", f32(&s));
printf("f33(&s) => %lld\n", f33(&s));
printf("s.a31 + s.b31 => %lld\n", s.a31 + s.b31);
printf("s.a32 + s.b32 => %lld\n", s.a32 + s.b32);
printf("s.a33 + s.b33 => %lld\n", s.a33 + s.b33);
printf(" a32 + b32 => %lld\n", a32 + b32);
return 0;
}
Using Clang on OS/X, I get this output:
f31(&s) => -1 f32(&s) => 4294967295 f33(&s) => -1 s.a31 + s.b31 => 4294967295 s.a32 + s.b32 => 4294967295 s.a33 + s.b33 => -1 a32 + b32 => -1
Using GCC on Linux, I get this:
f31(&s) => -1 f32(&s) => 4294967295 f33(&s) => 8589934591 s.a31 + s.b31 => 4294967295 s.a32 + s.b32 => 4294967295 s.a33 + s.b33 => 8589934591 a32 + b32 => -1
The above output shows 3 types of inconsistencies:
- different behavior for different compilers;
- different behavior for different bit-field widths;
- different behavior for inline expressions and equivalent expressions wrapped in a function.
The C Standard has this language:
6.7.2 Type specifiers
...
Each of the comma-separated multisets designates the same type, except that for bit-fields, it is implementation-defined whether the specifier
intdesignates the same type assigned intor the same type asunsigned int.
Bit-fields are notoriously broken in many older compilers...
Is the behavior of Clang and GCC conformant or are these inconsistencies the result of one or more bugs?
printfs. Until those are fixed, the code has undefined behavior, and is therefore allowed to do anything. - user3386109gcclooks like is masking every calculation onlong long : 33bit-fields with a2<<33-1mask before and after calculation.clangjust sign-extendsa33and usesraxto calculate it -clangdoesn't mask it with2<<33-1. I don't know if this is correct - should be along long : 33bitfield promoted tolong longor could be promoted to some implementation supported "__uint33_t"` type. - KamilCuklong longto get consistent results in the 4th, 5th, and 6thprintfcalls, e.g.(long long) (s.a31 + s.b31)Fixing this gives consistent results for the function calls vs. the inline computations, at least withgcc. - Tom Karzessigned int,unsigned,_Bool. Anything else is trouble. "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." C11 ยง6.7.2.1 5 - chux - Reinstate Monica