Let consider the following program test.c
:
#include <stdio.h>
struct test {
unsigned int a:5;
};
int main () {
unsigned int i;
struct test t = {1};
for (i = 0; i < t.a << 1; i++)
printf("%u\n", i);
return 0;
}
When compiled with gcc -Wsign-compare test.c
the following warning is produced (tested with gcc 4.8.1):
test.c:9:19: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
for (i = 0; i < t.a << 1; i++)
^
clang -Wsign-compare test.c
produces the following (tested with clang 3.2):
test.c:9:19: warning: comparison of integers of different signs: 'unsigned int' and 'int' [-Wsign-compare]
for (i = 0; i < t.a << 1; i++)
~ ^ ~~~~~~~~
1 warning generated.
Thus the right operand, a shifted unsigned bit field, becomes a signed int. This warning shows for any bit field value between 1 and 31 included. For higher values no warning is produced. This is weird.
This was tested with a bit field of type unsigned short
, unsigned int
and unsigned long
. The latter do not show any warning for bit field values between 32 and 64 included.
When no shifting is done there is no warning, hence the bit field is unsigned as expected.
Why bit fields of size lower than 32 bits become signed when shifted? I assume this is not a bug since this is consistent with both gcc
and clang
. I must be missing some info about how bit fields (or shifting) work but what? How can shifting an unsigned value yield a signed value?
Integer promotions are applied to the operands of shift covered in the draft C99 standard section
6.5.7
Bitwise shift operators paragraph 3 which says (emphasis mine going forward):and the integer promotion of a bit-field is covered in section
6.3.1.1
Boolean, characters, and integers paragraph 2 which says:and contains the following bullet:
and then says:
it was clarified in the draft C11 standard:
So this is expected behavior.