Why does a one-bit `int` bit-field have values 0 and −1?

Viewed 127

Why does this code produce the output shown?

    #include<stdio.h>
    struct sample{
        int x:1;
    }a[5]={0,1,2,3,4};
    int main(){
        int i;
        for(i=0;i<5;++i){
            printf("%d ",a[i].x);
        }
        return 0;
    }

The output when compiled and executed using OnlineGDB is:

0 -1 0 -1 0 
3 Answers

int x:1 acts as a signed one-bit integer. Per C 2018 6.7.2 5, an implementation may treat bit-fields declared as int as unsigned int, but the C implementation you are using appears to treated it as signed.

Additionally, the C implementation uses two’s complement. In two’s complement, an n-bit integer has 1 sign bit and n−1 value bits. Since your integer is one bit, it has one sign bit and zero value bits. Per the specification of two’s complement (C 2018 6.2.6.2 2), the value of the sign bit is −2M, where M is the number of value bits. Since that is zero, the value of the sign bit is −20 = −1.

Thus, the bit-field has value 0 when the bit is zero and value −1 when the bit is one.1

During initialization with ={0,1,2,3,4};, the first bit-field in the array is set to zero. However, the others overflow the values representable in the bit-field. Per C 2018 6.3.1.3 3, either an implementation-defined result is produced or an implementation-defined signal is raised. This C implementation appears to be producing the low bit of the source value as the value for the bit in the bit-field. This produces bits of 0, 1, 0, 1, and 0 in the five bit fields, representing values of 0, −1, 0, −1, and 0. Thus, when they are printed, the values 0, −1, 0, −1, and 0 result.

Footnote

1 This may be seen as a natural consequence of how two’s complement normally works: With eight bits, the range is −128…127. With four bits, the range is −16…15. With fewer and fewer bits, the range is −8…7, −4…3, −2…1, and finally −1…0.

The designation 'int x:1' means it consider only one bit of x (2^0 bit) as a range of this integer. That explains why the output is 0 and 1. The negative value is due to 'int' normally reserve the last bit (2^31) to designate if the number is negative or not. your in x:1 has one bit and that bit also indicate it is a negative number.

If you try out 'unsigned x:1' it will output a positive number because the last bit of you 'x:int' doesn't designate a negative number.

Try this program with 'int x:2' and 'unsigned x:2' and you see an interesting behavior.

It clarify why the range of int and unsigned are so different despite the they have the same size of bits:

int -> -2,147,483,648 to 2,147,483,647

unsigned -> 0 to 4,294,967,295

Important to note the bit position used used by ':' to limit the range is up to implementation and may give you other results.

First of all, using GCC 9.2.1, I see several -Woverflow warnings, for each initializer except 0 and 1 (which makes sense).

An initializer of -1 will also compile without warning. This is because it is (defined as) a one-bit field. Integers with all (one) bits set are equal to -1, unless they are unsigned. And the LSB (first bit) is never set for an even number, which explains why only odd numbers are non-zero.

Related