JS
C

JavaScript to C

10 lessons

Progress0%
1Variables & Types2Functions3Arrays & Pointers4Objects → Structs5Memory Management6Preprocessor & Headers7String Handling in Depth8Enums and Bitwise Operations9Bit-Fields and Unions10Multi-File Projects and Linking
All Mirror Courses
JS
C
Bit-Fields and Unions
MirrorLesson 9 of 10
Lesson 9

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.

Mirror Card
JS
From JavaScript:

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
In C:

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:

C
C 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
JavaScript (What you know)
// 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;
}
Mirror Card
JS
From JavaScript:

You may be used to different syntax or behavior.

C
In C:

Bit-field syntax: unsigned int name : N; — N bits wide inside a struct

Mirror Card
JS
From JavaScript:

You may be used to different syntax or behavior.

C
In C:

sizeof a bit-field struct is rounded up to the smallest integer type that fits

Mirror Card
JS
From JavaScript:

You may be used to different syntax or behavior.

C
In C:

union overlaps all members in the same memory; only one is valid at a time

Mirror Card
JS
From JavaScript:

You may be used to different syntax or behavior.

C
In C:

union size = size of its largest member; used for type-punning and variant records

Mirror Card
JS
From JavaScript:

You may be used to different syntax or behavior.

C
In C:

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.

JS
JavaScript
const s = { ready: false, error: false, size: 0 };
C
C
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.

C
C
Status s = {0};
s.ready = 1;
s.size  = 42;
printf("%u %u\n", s.ready, s.size);  /* 1 42 */
Common Pitfall
Bit-field layout (endianness, padding) is implementation-defined — never serialize bit-fields to binary files or network packets.

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
JavaScript
// JS: value can be number or string — runtime checks
function use(v) { return typeof v === 'number' ? v*2 : v.length; }
C
C
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
JavaScript
// JS discriminated union via type property
const v = { type: 'num', value: 42 };
C
C
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
Common Pitfall
Don't assume C works exactly like JavaScript. While the concepts may be similar, the syntax and behavior can differ significantly.

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
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your JavaScript code in C to practice these concepts.
PreviousNext