1

Inspired from this question, I have:

#include<stdio.h>
struct st
{
        int a:1;
        int b:2;
};

int main()
{
        struct st obj={1, 2};
        printf("a = %d\nb = %d\n",obj.a,obj.b);
}

and I get:

Georgioss-MacBook-Pro:~ gsamaras$ gcc -Wall main.c 
main.c:10:26: warning: implicit truncation from 'int' to bitfield changes value
      from 2 to -2 [-Wbitfield-constant-conversion]
        struct st obj={1, 2};
                          ^
1 warning generated.
Georgioss-MacBook-Pro:~ gsamaras$ ./a.out 
a = -1
b = -2

I think I understand why both bitfields fail to hold their values (as per this answer), but I don't understand why the compiler warns about 2 only, instead of 1 too! Any ideas?

I am using in my Mac:

Georgioss-MacBook-Pro:~ gsamaras$ gcc -v
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 8.1.0 (clang-802.0.38)
Target: x86_64-apple-darwin16.3.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

In an old Linux system, with gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5), I got no related warning.

In a Debian installation, with gcc version 4.9.2 (Debian 4.9.2-10) , I got no related warning!

Community
  • 1
  • 1
gsamaras
  • 71,951
  • 46
  • 188
  • 305

2 Answers2

1

https://en.wikipedia.org/wiki/Two%27s_complement

There could be a couple things that are happening, maybe some experiments can help.

First, it could be that gcc is smart enough to know that a single bit can't really be positive or negative because it's just the single bit.

The other possibility is the order in which gcc evaluates a expression is backwards from what you are thinking. Some compilers evaluate from right to left and in your case, kicked out the error and stopped.

To test, correct the bitfield for b to hold a signed integer for '2' without flipping the sign bit (3 bits should work). If in fixing 'b' you have generated an error for 'a', then you know it's just the compiler evaluation order.

If fixing 'b' doesn't cause 'a' to kick out a warning, then gcc is performing some internal optimization on a single bit field.

Also, changing your bitfield types to 'uint' should also fix the warnings, in which case it is simply the sign bit of the representation getting flipped.

Happy coding.

EDIT Relevant source code: https://github.com/llvm-mirror/clang/blob/master/lib/Sema/SemaChecking.cpp#L8812

James Poag
  • 2,320
  • 1
  • 13
  • 20
  • When I fix `b`, I get no warning at all for `a` . . .But +1 for the idea! – gsamaras May 10 '17 at 11:34
  • Cool. I would probably put money on gcc having a specialization for a single bit case. In C++ this is done using partial template specialization, and I *think* gcc is written in C++. – James Poag May 10 '17 at 11:38
  • 1
    I found the source code check for clang here: https://github.com/llvm-mirror/clang/blob/master/lib/Sema/SemaChecking.cpp#L8812 – James Poag May 10 '17 at 13:00
0

int b:2; is 2 bit width signed integer. Number 2 (unsigned) is represented as 10 in binary and because source int is signed, you actually get signed number saved to memory.

This is the reason you got this warning.

Values you can save to 2-bit signed int are: (-2 = 11, -1 = 10, 0 = 00, 1 = 01)

Reason why you did not get in both cases is that int a:1 is only 1 bit and can hold numbers 1 or 0, without negative numbers.

unalignedmemoryaccess
  • 7,246
  • 2
  • 25
  • 40