Bit in memory
X86 machine, OS: Linux 2.6 RH. Here are my codes:
#include "stdio.h"
typedef struct ch_t
{
int c0:1;
int c1:1;
int c2:1;
int c3:1;
int c4:1;
int c5:1;
int c6:1;
int c7:1;
} ch;
typedef union chh_u
{
char a;
ch chat;
} chh;
int main(void)
{
chh uu;
uu.a = 6;
printf("\n%d", uu.chat.c0);
printf("\n%d", uu.chat.c1);
printf("\n%d", uu.chat.c2);
printf("\n%d", uu.chat.c3);
printf("\n%d", uu.chat.c4);
printf("\n%d", uu.chat.c5);
printf("\n%d", uu.chat.c6);
printf("\n%d", uu.chat.c7);
printf("\n%d\n", uu.a);
return 0;
}
As I expected, the output should be:
0 0 0 0 0 1 1 0 6
But the actual result was:
0 -1 -1 0 0 0 0 0 6
I can't figure out why the output is above. I think the 6-bit order is 0000 0110, and in memory, in my version, the bit order is also 0000 0110. But the result shows the difference.
Can someone explain this?
source to share
The relevant parts of the standard are 6.7.2 (5).
Each of the comma-separated sets denotes the same type , except that for bit-fields it is implementation-defined: the specifier
int
denotes the same type assigned int
, or the same type asunsigned int
.
... this explains why you (can) get -1 instead of 1 for given bits. The other is 6.7.2.1 (10):
An implementation can allocate any address storage block large enough to store the bit field. If there is enough space left, the bit-field that immediately follows another bit-field in the structure should be packed into adjacent bits of the same block. If insufficient space remains, then whether a bit-field that does not fit fits into the next bits or overlaps adjacent ones is implementation-defined. The order in which the bit-fields are allocated within a device (from high order to low or low order) is implementation-defined. Storage address block alignment unspecified.
So this can go anyway too.
Addendum: Since there seems to be some confusion about this: calling a function with a variable argument list with bitfields is good if the function is expecting int
inside, for the same reasons that it's okay to say
char c = '\xff';
printf("%d\n", c); // will usually print 255 or -1
and it can give different results for the same reasons, because like a bitfield, a char
can be signed or unsigned depending on the implementation. I'll cite the relevant bits, with the irrelevant parts stripped out this time, because those rules are buried in the somewhat tediously legal parts of the C99 standard. They are found in 6.5.2.2 (6):
If the expression denoting the function being called is of a type that does not contain a prototype, integer actions are performed for each argument , and the type arguments are
float
converted todouble
. These are called default promotions. (...)
and 6.5.2.2 (7):
(...) The ellipsis notation in the function prototype declarator causes the argument type conversion to stop after the last declared parameter. By default, active declarations are executed when arguments are completed .
Targeted promotions are defined in 6.3.1.1; the corresponding bit is given in paragraphs 2 and 3:
2 For use
int
orunsigned int
can be used the following:
- (...)
- Bit field type
_Bool
,int
,signed int
orunsigned int
.If it
int
can represent all values ββof the original type, the value is converted to a valueint
; otherwise, it is converted tounsigned int
. (...)3 Integer stocks retain their value, including the sign. (...)
So this happens:
- Your compiler treats bitfields
int
as signed - This means that a 1-wide bitfield with its set of bits is -1; this, as they say, is only a sign, and it is installed.
- On transition to,
printf
it is converted, keeping this value, toint
- This int is printed
printf
according to the format string%d
and gives you "-1". - Profit?
source to share
Try to make values ββin your bitfield unsigned
. This avoids the unexpected behavior you experience.
typedef struct ch_t {
unsigned int c0:1;
unsigned int c1:1;
unsigned int c2:1;
unsigned int c3:1;
unsigned int c4:1;
unsigned int c5:1;
unsigned int c6:1;
unsigned int c7:1;
} ch;
source to share
This is where you create a variable signed int
[usually the int
default signed int
] bitfield
) and try to print the value using the format specifier %d
. Unfortunately this will not give you the correct value for single bit bit fields.
As @wintermute explains in his comment, when using bit variables directly in printf()
, they are counted signed integer
. OTOH, according to a rule, one bit signed integer
can be either 0
or -1
, and so in this case the representation will -1
.
Also, in your case, I assume you understand what c0
is LSB
and c7
will be MSB
, right?
The required bit must be removed before printing. Try something like below.
#include <stdio.h>
typedef struct ch_t
{
int c0:1;
int c1:1;
int c2:1;
int c3:1;
int c4:1;
int c5:1;
int c6:1;
int c7:1;
} ch;
typedef union chh_u
{
char a;
ch chat;
} chh;
int main(void)
{
chh uu;
uu.a = 6;
printf("\n%d", (uu.chat.c0) && 0x01);
printf("\n%d", (uu.chat.c1) && 0x01);
printf("\n%d", (uu.chat.c2) && 0x01);
printf("\n%d", (uu.chat.c3) && 0x01);
printf("\n%d", (uu.chat.c4) && 0x01);
printf("\n%d", (uu.chat.c5) && 0x01);
printf("\n%d", (uu.chat.c6) && 0x01);
printf("\n%d", (uu.chat.c7) && 0x01);
printf("\n%d\n", uu.a);
return 0;
}
Otherwise, use unsigned int
the datatype in your structure definition.
source to share
In struct ch_t
bit fields c0
.. c7
occupy the least significant 8 bits, both c0
as the lowest (2 ^ 0) and c7
as the highest (2 ^ 7). Set from highest to lowest, as if writing a binary number you have c7 c6 c5 c4 c3 c2 c1 c0
. The number 6 in 8 binary digits is 00000110
, therefore, means that c2
they c1
contain 1
bits, and the rest contain bits 0
. This matches the output you got where these fields were showing -1
.
The reason they show up as -1
instead 1
is because you used int
instead unsigned int
for your bitfields. This effectively gives you a 1 bit signed integer type which for 2 compliments can only contain 0 or -1.
source to share