Bit-Fields and Unions
C-specific: bit-field struct members pack multiple values into one word; union overlays multiple types in the same memory location.
Introduction
In this lesson, you'll learn about bit-fields and unions in C. Coming from JavaScript, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.
In JavaScript, you're familiar with c-specific: bit-field struct members pack multiple values into one word; union overlays multiple types in the same memory location..
C has its own approach to c-specific: bit-field struct members pack multiple values into one word; union overlays multiple types in the same memory location., which we'll explore step by step.
The C Way
Let's see how C handles this concept. Here's a typical example:
#include <stdio.h>
/* Bit-field: pack multiple small values into one word */
typedef struct {
unsigned int ready : 1; /* 1 bit */
unsigned int error : 1; /* 1 bit */
unsigned int size : 4; /* 4 bits (0-15) */
unsigned int _pad : 2; /* padding to 8 bits */
} StatusByte;
/* Union: different interpretations of the same memory */
typedef union {
int i;
float f;
char bytes[4];
} Data;
int main(void) {
StatusByte s = {0};
s.ready = 1;
s.size = 7;
printf("ready=%u size=%u\n", s.ready, s.size); /* ready=1 size=7 */
printf("sizeof(StatusByte)=%zu\n", sizeof(s)); /* typically 1 */
Data d;
d.i = 0x41424344;
printf("as float: %f\n", d.f); /* same bytes, different type */
printf("bytes: %c%c%c%c\n", /* DCBA (little-endian) */
d.bytes[3], d.bytes[2], d.bytes[1], d.bytes[0]);
return 0;
}Comparing to JavaScript
Here's how you might have written similar code in JavaScript:
// JS has no bit-fields; simulate with an object
const flags = { ready: true, error: false, size: 3 };
// Union-like: a value that can be one of several types
function processValue(val) {
if (typeof val === "number") return val * 2;
if (typeof val === "string") return val.toUpperCase();
return null;
}You may be used to different syntax or behavior.
Bit-field syntax: unsigned int name : N; — N bits wide inside a struct
You may be used to different syntax or behavior.
sizeof a bit-field struct is rounded up to the smallest integer type that fits
You may be used to different syntax or behavior.
union overlaps all members in the same memory; only one is valid at a time
You may be used to different syntax or behavior.
union size = size of its largest member; used for type-punning and variant records
You may be used to different syntax or behavior.
JavaScript has no equivalent — JS objects are heap-allocated hash maps, not packed memory layouts
Step-by-Step Breakdown
1. Declare a Bit-Field Struct
Add : N after the member name to allocate exactly N bits. The struct's byte size is the smallest integer type that accommodates all fields.
const s = { ready: false, error: false, size: 0 };typedef struct {
unsigned int ready : 1;
unsigned int error : 1;
unsigned int size : 6; /* 0-63 */
} Status;2. Read and Write Bit-Fields
Bit-fields are accessed like ordinary struct members; the compiler handles the masking and shifting.
Status s = {0};
s.ready = 1;
s.size = 42;
printf("%u %u\n", s.ready, s.size); /* 1 42 */3. Declare and Use a Union
All union members share the same memory. Writing one member and reading another is type-punning — legal for inspecting raw bytes.
// JS: value can be number or string — runtime checks
function use(v) { return typeof v === 'number' ? v*2 : v.length; }typedef union { int i; float f; unsigned char b[4]; } Word;
Word w;
w.f = 3.14f;
printf("bits: %08X\n", w.i); /* raw float bits as hex */4. Tagged Union (Variant Type)
Combine a union with a tag enum to build a safe variant — the idiomatic C approach to sum types / discriminated unions.
// JS discriminated union via type property
const v = { type: 'num', value: 42 };typedef enum { T_INT, T_FLOAT, T_STR } Tag;
typedef struct {
Tag tag;
union { int i; float f; const char *s; } val;
} Value;
Value v = { .tag = T_INT, .val = { .i = 42 } };
if (v.tag == T_INT) printf("%d\n", v.val.i);Common Mistakes
When coming from JavaScript, developers often make these mistakes:
- Bit-field syntax: unsigned int name : N; — N bits wide inside a struct
- sizeof a bit-field struct is rounded up to the smallest integer type that fits
- union overlaps all members in the same memory; only one is valid at a time
Key Takeaways
- Bit-fields: unsigned int name : N; packs N bits inside a struct — useful for hardware registers and flags
- union overlaps all members; only one is valid at a time — use for type-punning or variant records
- Tagged union = union + enum tag; the idiomatic C discriminated union
- Never rely on bit-field binary layout across compilers — implementation-defined