#include <stdio.h>
#include <stdint.h>
#include <limits.h>
#define MASK(pos, len) (((1ULL << (pos + len)) - 1) >> pos) << pos
uint32_t extract_field(uint32_t reg, uint8_t pos, uint8_t len)
{
uint32_t extract_value = 0;
extract_value = (reg & MASK(pos, len)) >> pos;
return extract_value;
}
int main() {
uint32_t reg;
uint8_t pos, len;
scanf("%u %hhu %hhu", ®, &pos, &len);
printf("%u", extract_field(reg, pos, len));
return 0;
}This code provides a generic way to extract a bit-field (a specific sequence of bits) from a 32-bit register. It is essentially a more flexible version of the "nibble extractor" you saw earlier, as it allows you to define exactly where the field starts and how long it is.
MASK(pos, len)The heart of the program is the macro that generates a "window" of 1s.
1ULL << (pos + len): This shifts a 1 to the left past the end of our desired field. We use 1ULL (Unsigned Long Long) to prevent overflow if the field extends to the 31st bit.
- 1: Subtracting 1 turns all bits below that position into 1s.
>> pos) << pos: Similar to the range-setter, this "chops off" the 1s below our starting position.
Example: For pos=2, len=3, the macro creates a mask where bits 2, 3, and 4 are 1 (00011100).
extract_fieldThis function uses the generated mask to pull the data out.
Bitwise AND (reg & MASK): This "silences" every bit in the register except for the ones inside our window.
Right Shift (>> pos): The isolated bits are still floating in the middle of the register. This shift moves them all the way to the right (starting at bit 0). This converts the "raw" bit pattern into a standard numerical value.
Imagine a 32-bit register representing a hardware status. Bits 4, 5, and 6 represent a "Sensor ID".
Input: reg = 114 (Binary: ...01110010), pos = 4, len = 3.
Step | Operation | Result (Binary) |
|---|---|---|
Mask Generation |
|
|
Isolation (AND) |
|
|
Alignment (Shift) |
|
|
Final Result | Return 7 | The Sensor ID is 7. |
Bit-Field Isolation: This is the standard way to read "sub-variables" packed into a single register, which is very common in automotive (CAN/UDS) and embedded systems.
Type Safety: Using uint32_t ensures the register is treated as 32 bits, and the 1ULL in the macro is a "safety first" practice to handle shifts that might otherwise exceed the size of a standard integer.
Efficiency: This approach uses only three CPU operations (Shift, Subtract, AND), making it significantly faster than using a struct with bit-fields in some compilers.
Input
3060793344 28 4
Expected Output
11