mirror of
https://github.com/pkivolowitz/asm_book.git
synced 2026-06-21 08:06:48 +08:00
111 lines
3.3 KiB
Markdown
111 lines
3.3 KiB
Markdown
# Section 2 / Bit Fields
|
|
|
|
## Overview
|
|
|
|
Many C and C++ programmers have never seen bit fields.
|
|
|
|
Bit fields are a
|
|
feature of the C and C++ language which completely hide what is often
|
|
called "bit bashing".
|
|
|
|
Bit bashing is the manipulation of individual bits. Bit
|
|
bashing goes to the very core of the C language. Remember that C is a
|
|
high level assembly language, as we argue in Section 1 of this book.
|
|
And C is the (later) language in which Unix was implemented and indeed,
|
|
C was
|
|
developed specifically to implement Unix.
|
|
|
|
Since an operating system directly
|
|
interfaces with hardware - the C language grew to have features
|
|
to aid Unix implementers.
|
|
|
|
*With that said, consider this WARNING: the ordering of bits in a bit
|
|
field is not guaranteed to be the same on different platforms and even
|
|
between different compilers on the same platform.*
|
|
|
|
Bit fields are implemented within a `struct` by appending a colon plus
|
|
a number after the declaration of integer types.
|
|
|
|
For example:
|
|
|
|
```c
|
|
struct BF {
|
|
unsigned char a : 1;
|
|
unsigned char b : 2;
|
|
unsigned char c : 5;
|
|
};
|
|
```
|
|
|
|
The above declares a `struct` whose size is 1 byte. Members of the `struct`
|
|
are `a`, `b` and `c` which are 1, 2 and 5 bits in size, respectively.
|
|
|
|
## Bit Fields Aren't Just For Hardware
|
|
|
|
Consider a data structure for which there will be potentially millions of
|
|
instances in RAM. Or, perhaps billions of instances on disc. Suppose you
|
|
need 8 boolean members in every instance. The C++ standard does not
|
|
define the size of a `bool` instead leaving it to be implementation
|
|
dependent. Some implementations equate `bool` to `int`, four bytes in
|
|
length. Some implement `bool` with a `char`, or 1 byte in length.
|
|
|
|
Let's assume the smallest case and equate a `bool` with `char`. Our
|
|
`struct`, for which there may be millions or billions of instances
|
|
requires 8 `bool` so therefore 8 bytes. Times millions or billions.
|
|
|
|
Ouch.
|
|
|
|
Bit fields can come to your aid here by using a single bit per boolean
|
|
value. In the best case, 8 bytes collapse to 1 byte. In a worse case,
|
|
8 x 4 = 32 bytes collapsed into 1.
|
|
|
|
## Without Bit Fields
|
|
|
|
Let's assume we're working with a byte that is comprised of three
|
|
fields layed out as in `struct BF` above. That is, a one, two and
|
|
five bit field inside one byte.
|
|
|
|
Without bit fields, we would have to write this code:
|
|
|
|
```c
|
|
/* Note the absence of defensive programming such as checking
|
|
to ensure that byte is not null and that bit_number is not
|
|
too large.
|
|
*/
|
|
|
|
void ClearA(unsigned char * byte) {
|
|
*byte &= ~1;
|
|
}
|
|
|
|
void SetA(unsigned char * byte) {
|
|
*byte &= ~1;
|
|
*byte |= 1;
|
|
}
|
|
|
|
void ClearB(unsigned char * byte) {
|
|
*byte &= ~6;
|
|
}
|
|
|
|
void SetB(unsigned char * byte, unsigned char value) {
|
|
value &= 3; // ensures only bits 0 and 1 can be set
|
|
*byte &= ~6; // clears bits 1 and 2 in byte
|
|
*byte |= (value << 1); // stores bits 0 and 1 into bits 2 and 3
|
|
}
|
|
|
|
void ClearC(unsigned char * byte) {
|
|
*byte &= 7; // squashes bits 3 to 7 to 0
|
|
}
|
|
|
|
void SetC(unsigned char * byte, unsigned char value) {
|
|
value &= 0x1F; // ensures only bits 0 to 4 can be set
|
|
*byte &= ~(0x1F << 3); // squashes correct bits in byte
|
|
*byte |= (value << 3); // or's in the bits at the right place
|
|
}
|
|
```
|
|
|
|
In naive assembly language, these functions would look like this:
|
|
|
|
```asm
|
|
```
|
|
|
|
|
|
|