views:

614

answers:

7

I need to pack some bits in a byte in this fashion:
struct
{
char bit0: 1;
char bit1: 1;
} a;
if( a.bit1 ) /* etc */

Or:
if( a & 0x2 ) /* etc */

From the source code clarity it's pretty obvious to me that bitfields are neater. But which option is faster? I know the speed difference won't be too much if any, but as I can use any of them, if one's faster, better.
On the other hand, I've read that bitfields are not guaranteed to arrange bits in the same order across platforms, and I want my code to be portable.

Notes: If you plan to answer 'Profile' ok, I will, but as I'm lazy, if someone already has the answer, much better.
The code may be wrong, you can correct me if you want, but remember what the point to this question is and please try and answer it too.

+4  A: 

If you want portability, avoid bitfields. And if you are interested in performance of specific code, there is no alternative to writing your own tests. Remember, bitfields will be using the processor's bitwise instructions under the hood.

anon
Why, other than bitfield ordering, would bitfields be non-portable? Also some processors do support bitfield insert/extract. The 68020 did, for example.
Richard Pennington
There are also packing issues. And just because a processor has a special purpose instruction, there is no guarantee that the compiler uses it - that's why I said a test is essential.
anon
Ah, but just because a bitfield might use more or less space depending on the implementation doesn't make it non-portable. And if a compiler doesn't take advantage of what the processor can do, it should be improved or replaced.
Richard Pennington
@Richard: What if it's not worth using those instructions? The x86 instruction set for example, is littered with instructions that AMD's and Intel's optimization manuals simply describe with "For best performance, don't use these instructions". Almost everything about bitfields are implementation-defined, so relying on them to have the same layout across platforms is pretty much suicidal.
jalf
@jalf: If an operation is more expensive of course the compiler should not use it. I'm not suggesting it should. My point is that bitfields are not non-portable if used for space savings. If you try to count on bitfield ordering, then you're out of luck (see my answer).
Richard Pennington
Depends on what kind of portability we're talking about. Of course the code will work if you compile and run it on another platform, but the layout of a struct with bitfields isn't (in C++) guaranteed to be consistent between platforms. It's also not guaranteed to save you any space.
jalf
@jalf: You can't portably rely on struct layout between platforms, bitfields or not. I challenge you to name a compiler for which bitfields don't save space. Sometimes pragmatism is a good thing.
Richard Pennington
Some compilers can recognize bit field operations and use the processor's bit field operations. I know that GCC does this for the AVR, to the point that the AVRlibc people only provide bitfield functions implemented in assembly as support for legacy code, and recommend that new code use the `REG |= 0x08;` or `REG ` (for example) constructs. This kind of compiler optimization is similar to a compiler replacing integer multiply and divide by powers of 2 with bit shifts.
Mike DeSimone
+1  A: 

I think a C programmer would tend toward the second option of using bit masks and logic operations to deduce the value of each bit. Rather than having the code littered with hex values, enumerations would be set up, or, usually when more complex operations are involved, macros to get/set specific bits. I've heard on the grapevine, that struct implemented bitfields are slower.

James Morris
More to the point, bitfields are not guaranteed to be in order across platforms. So if you're using a bitfield for a hardware register, you will have a very difficult time debugging why your hardware is not doing what it should be.
Sam Post
+2  A: 

The first one is explicit and whatever the speed the second expression is error-prone because any change to your struct might make the second expression wrong.

So use the first.

Klaim
No. The first one uses an unusual feature that most coders never touch and do not understand, while the second is plain idiomatic C. So it is the second option that is both clearer about its intention, and less likely to be broken by careless maintainers.
Porculus
The only thing that's wrong with the second expression is that `0x02` needs to be a defined constant so a developer knows what it means. The thing that is wrong with the first are that the compiler can put the two bits into the 8-bit byte wherever it wants. The Linux kernel defines a whole set of constants to declare bit order and packing, which leads to hard-to-maintain `#ifdef` blocks. And that's assuming the platform defines `char` as 8 bits; I've developed for platforms that defined `char` as 16 bits because the DSP couldn't address bytes, just words.
Mike DeSimone
+5  A: 

Bitfields make the code much clearer if they are used appropriately. I would use bitfields as a space saving device only. One common place I've seen them used is in compilers: Often type or symbol information consists of a bunch of true/false flags. Bitfields are ideal here since a typical program will have many thousands of these nodes created when it is compiled.

I wouldn't use bitfields to do a common embedded programming job: reading and writing device registers. I prefer using shifts and masks here because you get exactly the bits the documentation tells you you need and you don't have to worry about differences in various compilers implementation of bitfields.

As for speed, a good compiler will give the same code for bitfields that masking will.

Richard Pennington
Problem with using bitfields is that you have no guarantee it'll save any space. The compiler might just allocate 32 bits for each bitfield, as if it'd been an int. Why wouldn't it? It'll be faster in most cases.
jalf
Right. but it could save space. Any many smart people count on bitfields to do just that.
Richard Pennington
@jaif: I call BS. Do you have anything that supports that assertion, I'm too lazy to look at the spec, but I really doubt that is allowed.
SoapBox
@Richard And many even smarter people don't - to quote Brian Kernighan in The Practice Of Computer Programming - "Bitfields are so machine-dependent that no-one should use them"
anon
@Niel: If you try to rely on bitfield ordering, then they are non-portable, as I've said all along. They are not non-portable in and of themselves.
Richard Pennington
@SoapBox From the C++ Standard: "Allocation of bit-fields within a class object is implementation-defined." So an implementation is allowed to do just what Jalf said.
anon
@Neil: That's not what the C standard says. "An implementation may allocate any addressable storage unit large enough to hold a bit-field. If enough space remains, a bit-field that immediately follows another not-field in a structure shall be packed into adjacent bits of the same unit."
Richard Pennington
@Richard Thus illustrating the futility of tagging things as both C and C++.
anon
@Jalf: You are wrong. The standard says "Values stored in bit-fields consist of m bits, where m is the size specified for the bit-field." An implementation is not free to ignore m.
Richard Pennington
@Niel: But it makes the arguments more fun. ;-)
Richard Pennington
@Richard: One word: padding. The (C++) standard doesn't say that the next bitfield should follow immediately.
jalf
@Neil, tagging both C and C++ has the advantage of making people are of the difference. (I doubt there is a practical difference here, ABI for C++ are based on the one for C for such things).
AProgrammer
@jalf: Actually I was surprised that the C standard did. I would have thought that the semantics would have been that the value is limited to the number of bits, without requiring any layout.
Richard Pennington
Reading the relevant sections of both standards, I've trouble to find something allowed in one and not in the other (well the C standard describes what is meant by "packed" while the C++ one takes it for granted, but that's all).
AProgrammer
@Richard: In your answer, I think you should also mention that bitfields shouldn't be used to extract bits in file/communications I/O. I agree with you that it can make code much clearer when used completely internally within a program.
Emile Cormier
+3  A: 

C bitfields were stillborn from the moment they were invented - for unknown reason. People didn't like them and used bitwise operators instead. You have to expect other developers to not understand C bitfield code.

With regard to which is faster: Irrelevant. Any optimizing compiler (which means practically all) will make the code do the same thing in whatever notation. It's a common misconception of C programmers that the compiler will just search-and-replace keywords into assembly. Modern compilers use the source code as a blueprint to what shall be achieved and then emit code that often looks very different but achieves the intended result.

Thorsten79
+1  A: 

I would rather use the second example in preference for maximum portability. As Neil Butterworth pointed out, using bitfields is only for the native processor. Ok, think about this, what happens if Intel's x86 went out of business tomorrow, the code will be stuck, which means having to re-implement the bitfields for another processor, say RISC chip.

You have to look at the bigger picture and ask how did OpenBSD manage to port their BSD systems to a lot of platforms using one codebase? Ok, I'll admit that is a bit over the top, and debatable and subjective, but realistically speaking, if you want to port the code to another platform, its the way to do it by using the second example you used in your question.

Not alone that, compilers for different platforms would have their own way of padding, aligning bitfields for the processor where the compiler is on. And furthermore, what about the endianess of the processor?

Never rely on bitfields as a magic bullet. If you want speed for the processor and will be fixed on it, i.e. no intention of porting, then feel free to use bitfields. You cannot have both!

Hope this helps, Best regards, Tom.

tommieb75
+1  A: 

Don't read to much in the "non portable bit-fields". There are two aspects of bit fields which are implementation defined: the signedness and the layout and one unspecified one: the alignment of the allocation unit in which they are packed. If you don't need anything else that the packing effect, using them is as portable (provided you specify explicitly a signed keyword where needed) as function calls which also have unspecified properties.

Concerning the performance, profile is the best answer you can get. In a perfect world, there would be no difference between the two writings. In practice, there can be some, but I can think of as many reasons in one direction as the other. And it can be very sensitive to context (logically meaningless difference between unsigned and signed for instance) so measure in context...

To sum up, the difference is thus mostly a style difference in cases where you have truly the choice (i.e. not if the precise layout is important). In those case, it is a optimization (in size, not in speed) and so I'd tend first to write the code without it and add it after if needed. So, bit fields are the obvious choice (the modifications to be done are the smallest one to achieve the result and are contained to the unique place of definition instead of being spread of to all the places of use).

AProgrammer