tags:

views:

156

answers:

7
int main ()  
{  
struct bit{  
 char f1:1;  
 char f2:1;  
};  
struct bit b;  
b.f1= 0x1;   
b.f2 = 0x1;   
printf("%d\n",b.f1);  
return 0;  
} 

compiled using gcc the code outputs -1. Should it not be 1? Is it because I am compiling on a little endian machine?

Added:While debugging using GDB i see that the value after initializing the struct members is -1. i.e. it is -1 before printing. Following is the printout from GDB:
(gdb) p b
$7 = {f1 = -1 '', f2 = -1 ''}

Let me know if you need any more debug commands. Please provide the commands for doing so.

A: 

bitfields are supposed to be typically 'unsigned xxxxx'.

[edit]
The reason for that is just what you encountered; somebody using a bitfield in an 'unusual' way, getting results that they should not get.

The point of a bitfield is to reflect a bit. What values are in a bit? 0 and 1 (I'm only speaking of normal bits, I can't grok the quantum stuff). Yet, you have found a way to insert -1, 0, 1 into that very same field. Somewhere it's got to break. I believe that a lot of bitfield confusion results in negative bitfields and so the unsigned modifier eases that confusion.

When you define your bitfield as an int, you can have negative values. That is the reason for your results above.
Also, please see here for a more detailed engagement of this topic. Note that this has been rehash innumerable times on SO and so a search for 'bitfield unsigned' would prove quite instructive
[/edit]

To 'R.', re: "signedness ... is implementation-defined, but it can't vary from one element to another."

#include <stdio.h>
int main ()
{
   struct bit{
      char f1:1;
      unsigned char f2:1;
   };
  struct bit b;
  b.f1 = 1;
  b.f2 = 1;
  printf("%d\n",b.f1);
  printf("%d\n",b.f2);
  return 0;
}

produces, as output:

-1
1
KevinDTimm
That is not true
sandeep
for the reasons stated in my answer, yes it is.
KevinDTimm
-1 No he hasn't inserted -1, 0, 1 in the bitfield, his bitfield represents only 2 values -1 and 0. Nothing more, nothing less. On another compiler he may have gotten 0 and 1. This is a choice of the compiler `char` is either `signed` or `unsigned`. This is the reason why one should be careful when indexing with `char`variables as they may sign extend and break code (gnu C warns for example when using something like `isalpha(c)` for that reason).
tristopia
I don't think so. Maybe for this bitfield, he gets -1 and 0, but for another in the same struct, he may get 1 and 0. I still contend that, for reasons of clarity, 0 and 1 are better than 0 and -1.
KevinDTimm
It's only dependend of the signedness of the type used, nothing else. There are scenarios when one or the other is the more logical thing to do. Maybe he wants to interface it with VB where booleans are 0 and -1. btw: on a 1 complement machine he would get 0 and -0.
tristopia
@KevinDTimm - I'm gonna assert that your first answer was the best. This issue wouldn't have appeared if the chars were explicitly set to unsigned. That's what the OP was supposed to do. Its unfortunate that this behavior is easy to bump into, IMHO. I can't think of too many situations where you want a bit to equal -1.
sheepsimulator
@sheepsimulator indeed it would have solved his immediate problem but the real cause of the problem was not understood. It would have only cured a symptom, but not cured the real problem, the implementation dependend signedness of the `char` type.
tristopia
@tristopia - agreed, the implementation dependency is typically the issue here. though the standard says, signed/unsigned and bool are allowed, they tend to confuse the bejeebers out of people, hence my original statement (I'm wrong re: standards, I'm correct re: usability and clarity)
KevinDTimm
@KevinDTimm: signedness (or even existence, I believe..?) of `char` bitfield elements is implementation-defined, but it can't vary from one element to another. Either they're all signed 1-bit values or all unsigned 1-bit values.
R..
@R - see my code sample posted above
KevinDTimm
A: 

I don't know C very well. For example, I don't know what the :1 does in char f1:1, but I was able to get this code to work on my PC by removing that:

#include <stdio.h>

int main ()
{
 struct bit{
  char f1;
  char f2;
 };

 struct bit b;

 b.f1 = 0x1;
 b.f2 = 0x1;
 printf("%d\n",b.f1);
 return 0;
}

Output below:

chooper@brooklyn:~/test$ gcc -o foo foo.c
chooper@brooklyn:~/test$ ./foo
1

I hope this helps you!

Charles Hooper
@Charles Hooper. Thanks for the effort. I was able to get the same output in a different machine. I investigated a little more and found that the big endian machine outputs 1 and the little endian machine outputs -1. The C snippet I used to test endianness is as follows: int a=1; char *p = (char *) if (*p==1) printf("little endian"); else printf("big endian"); Can you test it on your machine and check its endianness. Thanks again for the effort
sandeep
sandeep it has nothing to do with the endianness, it really depends on the choice of the compiler to consider `char` signed or unsigned. Some compiler have even a command line option to choose (Microsoft compilers if I remember correctly).
tristopia
I'm on a little endian machine, for some reason that completely slipped my mind
Charles Hooper
So that proves (as @tristopia also stated in his answer above) that it depends on how a compiler treats 'char'- signed or unsigned. But I think unless explicitly specified 'char' means 'signed char'. For some reason the compiler on one of the machines that i used treats it as unsigned(the one that outputs 1 and it was not gcc). I guess that should be a compiler bug. By the way Charles which compiler did you use for compiling?
sandeep
This is what the standard C99 says about `char`:The three types `char`, `signed char`, and `unsigned char` are collectively calledthe character types. The implementation shall define `char` to have the same range, representation, and behavior as either `signed char` or `unsigned char`.
tristopia
gcc 4.4.3 on x86_64 (linux)
Charles Hooper
There's also a note to that quote which is: `CHAR_MIN`, defined in <limits.h>, will have one of the values 0 or `SCHAR_MIN`, and this can be used to distinguish the two options. Irrespective of the choice made, `char` is a separate type from theother two and is not compatible with either.
tristopia
+8  A: 

char can be an unsigned or can be a signed type, it's up to the compiler to decide. In your case it's apparently signed so when you print your bit field the compiler extends the sign to the size of the int your bit is extended to. In 2 complement representation one should not forget that -1 is represented as every bit set 11111111 11111111 11111111 11111111 is -1 on 32 bit int. When you have only 1 bit you can represent only 2 values in 2 complement: 0 and 1 which is the binary representation of 0 and -1.

EDIT: Here the actual section of the C standard Chapter 6.2.5 section 15 : The three types char, signed char, and unsigned char are collectively called the character types. The implementation shall define char to have the same range, representation, and behavior as either signed char or unsigned char.35

35) CHAR_MIN, defined in limits.h, will have one of the values 0 or SCHAR_MIN, and this can be used to distinguish the two options. Irrespective of the choice made, char is a separate type from the other two and is not compatible with either.

tristopia
good ans. That makes sense(I did think on these lines but got confused by the different machine that printed 1. The difference in the two machine was endianness and got stuck in that rather than thinking about the C fundamentals. stupid me !). To assign positive 1 I will have to explicitly declare it as unsigned char. As far as I remember 'char' is always treated as signed and not unsigned. Is this implementation dependent or a standard? If it is a standard i can probably inform the compiler developer of the bug. Thanks a lot for your time and effort on this.
sandeep
the compiler that printed 1 is C89 compliant and not C99 compliant
sandeep
@sandeep: Wrong. Whether plain `char` is signed or unsigned (or more importantly, the behavior of a bitfield of type `char`) is implementation-defined. Both C89 and C99 agree on this.
R..
+3  A: 

Your bitfields are one bit wide and are signed values. The top most bit usually denotes the sign of the value, so, setting a 1 to the one bit wide signed value sets the sign bit so that reading the value gets you a -1.

Skizz
A: 

I believe KevinDTimm is right - bit fields are usually unsigned name:x.

This is what I think is happening: processors determine sign from the first bit of the field. gcc chars are signed (there is an unsigned char, so your code actually turns out to be:

struct bit 
{
   char f1;
   char f2;
};

And each char is one byte(4 bits), with the topmost byte indicating the sign (1=neg,0=pos). BUT since your fields are one bit, that's the sign bit. and you set it to be negative.

Hope this helps. If it's wrong please comment so I can fix it.

Michael
No, bitfields can be signed or unsigned, it depends on what you want to do, there's no rule for that.
tristopia
A: 

The above explanations are great. To make your program work, 1 possible solution may be to make the bit fields of type unsigned int.

smartmuki
+1  A: 

To add to the correct answers of Skizz and tristopia: for C99, the modern C, bit fields should be of type signed or unsigned int or of type bool (aka _Bool). All other types may be allowed by some platform but are not necessarily portable. What is even worse, is that even if you specify them as plain int the result may be signed or unsigned. So better stick to bool if you just need a flag and to unsigned when you need more than one bit.

Jens Gustedt