Click here to Skip to main content
15,867,686 members
Please Sign up or sign in to vote.
3.00/5 (1 vote)
The following code results in "5,3", while i expect it to result in "0,2". Essentially, this is why we use bitfields!
void f2()
{
	struct s1
	{
		short a:9;
		char b:4;
	};
	union s2
	{
		s1 c;
		short d;
	};

	s2 e;
	e.c.b = 5;
	e.d = 0;  // i expect that a & b of c both get zero
	cout << (int) e.c.b;  // but b doesn't

	cout << "," << sizeof(s1) << endl;  // because d doesn't cover b
}

any idea?
Posted

Many aspects of bitfields implementation are compiler dependent, and this creates some problem when packing values because the layout is undefined.
Anyway one main rule that the many compiler use is to differentiate between data types for packing. Meaning that is packed while in the same type, if the type change also fields start a new memory. I.e. if you change your declaration as:
C++
struct s1
{
    short a:9;
    short b:4;
};

this will give you an output 0,2 as you would expect.
In the original declaration the compiler instantiate a short using only 9 bits, then instantiate a char, using only 4 bits, for the second variable.
This explain why you get a size of 3 bytes, but still don't explain why you cannot zero the variable value. So I decided to make a debug tour in Visual Studio going into assembly to understand the reason for the strange behavior, and I found it. The memory layout is:
+-+-+-+-+-+-+-+-+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
\_______ _______/
        v
 +------+-----> a = 9bits: 8 from the lower one plus 1 from higher byte
 ^
+-+-+-+-+-+-+-+-+
|0|1|2|3|4|5|6|7| (bits from 1 to 7 not used)
+-+-+-+-+-+-+-+-+
+-+-+-+-+-+-+-+-+\
|0|1|2|3|4|5|6|7| \ (bits 4 to 7 not used)
+-+-+-+-+-+-+-+-+  |
\___ ___/          |
    v              > d = These 2 bytes are used for the short
 b = 4 bits        |     in the union.
+-+-+-+-+-+-+-+-+  |
|0|1|2|3|4|5|6|7| /
+-+-+-+-+-+-+-+-+/

And this explain all.
When using the bit-fields you choose a type to define the memory space to use, that declare all variables that will share that space.
I.e.
C++
struct MyStruct
{
  char a:2;
  char b:2;
  char c:1;
  char d:3;
  int  e:8;
  int  f:7;
  int  g:17;
}

Will occupy 5 bytes, the first for a, b, c, and d. Then the following int (4 bytes) will hold e, f and g.
 
Share this answer
 
v3
Comments
ilostmyid2 8-Jun-15 8:32am    
thanks for your investigation and description. this describes in details what actually is happening, but doesn't explain what to do to reach the same behavior i expect with the same data types. if i'm not wrong, when i was programming in BC++, it would behave correctly. regardless of data types, i've defined explicitly how many bits the variable should occupy. wasting other bits is not justifiable. then, the bits should be looked as the data types i've specified.
anyone knows how to change the vs 2010 compiler behavior to act correctly?!
Frankie-C 8-Jun-15 9:11am    
My first expectation was the same as you, but unfortunately we have not considered that the compiler cannot emit a continuous stream of bits strictly chained each other, so it uses the bit-field type change to break between storage parts. In fact the type of bit-field is used to define the size of container storage, and as per ANSI standard the bit-field cannot exceed the size of the type.
The same standards (C99, C11, C++11) says that the way the data is layed up in memory is compiler dependent, so we cannot force the compiler behavior to what is correct for us.
I have also noticed that you have already set packing to 1 because you got a result of 3 bytes, without it the space used is 4 bytes (for reason of bus access on odd addresses). Anyway the packing acts only on alignement between structure fields not on bit fields.
I started programming on DEC machines and have used structures to define hardware registers (where group of bits in the same register word had specific meanings) and had never had this problem. But I used always the same type for bit-fields, that solve your problem also.
I made some checks on different compilers, and the result is more or less the same: if you change the type it moves to next storage byte (some compiler doesn't even accept shorts and chars as types, but strictly adhere to ANSI standard that provides for signed and unsigned integers).
I'm sorry, but there is no other workaround for MSVC that using the same type for bit-fields that have to be in the same storage area. But what is your problem anyway? A char is a signed integer max size = 8bits and short is a signed integer too, but 16 bits max size. Declaring both as short works well afterall...

P.S. Reading again your answer I would advice you: have you noticed that you are not using same data type in bit-field (a char and a short)!
ilostmyid2 8-Jun-15 9:42am    
i prefer not to use the same types to indicate for the compiler what variables must group. for example, i want to set one to 'bool' to assign 'false' or 'true' to it instead of integral values.
since you said that you're certain that there's no way to tell MSVC to change this behavior, i mark your answer as the solution, but i'm still angry that there's no way.
now i'm going to test it in gcc. did u examine BC?
ilostmyid2 8-Jun-15 9:54am    
haha, g++ works great. This is the difference between a professional compiler and a commercial one. No Microsoft! Everything is not business. Try to be a bit professional. I tried the code in Ubuntu 12.04. The g++ outputs 0,2 as i expected. Shame on you, Microsoft! Ah!
ilostmyid2 8-Jun-15 9:58am    
wow man! i even changed places of a and b in s1 and the result is still 0,2! haha! this is the compiler i'm looking for.
i had discovered another elegant capability in gcc before. 0b001100.. is meaningful for gcc, while it's not for msvc. and again another capability is now discovered.
You must understand unions in C. Here is a tutorial. The member share memory, so you can do strange and buggy things with.

A short is only 2 bytes.
And sizeof return bytes, so (9+4) / 8 and alignment.
 
Share this answer
 
Comments
ilostmyid2 8-Jun-15 8:37am    
i think i know enough about bitfields after 20 years of programming in C. the fact is that the compiler acts incorrectly. the behavior in unexpected. it seems that MS doesn't believe in packing. as the default structures member alignment is not 1 byte.
i need an option to change this behavior.
any idea?

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

  Print Answers RSS
Top Experts
Last 24hrsThis month


CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900