Skip to Content
Course content

118: Bit Utilities with stdbit.h (C23)

Click on the "Edit" button in the top corner of the screen to edit your slide content.

For years, if you wanted to count leading zeros or find the index of the first set bit in C, you had to dive into compiler-specific intrinsics. You'd find yourself using __builtin_clz in GCC or _BitScanForward in MSVC. It was a mess of #ifdefs. With C23, we finally have <stdbit.h>. It standardizes these operations, making your code portable without sacrificing the performance of those hardware-accelerated instructions.

I want to show you how this works by building a tiny "Resource Slot Allocator." Imagine we have 64 possible slots for a hardware resource, and we're using a uint64_t bitmask to track which slots are currently free (1 for free, 0 for occupied).

Mapping our available slots

First, let's set up our state. We'll use an unsigned 64-bit integer. In a real system, this might be a memory map or a set of available DMA channels.

#include <stdio.h>
#include <stdint.h>
#include <stdbit.h>

typedef uint64_t slot_mask_t;

void allocate_slot(slot_mask_t *mask) {
    if (*mask == 0) {
        printf("No slots available!\n");
        return;
    }
    // We need to find the index of the first available bit
}

The "Wrong End" mistake

Now, I want to find the first available slot. I'll start with the lowest index (the least significant bit). My first instinct is to look for the zeros at the start of the word. I'll use stdc_leading_zeros_unz.

// WRONG APPROACH
int slot = stdc_leading_zeros_unz(*mask); 
printf("Allocating slot: %d\n", slot);

Wait. I just realized my mistake. stdc_leading_zeros_unz counts from the most significant bit (the left side). If my mask is 0x0000000000000001, stdc_leading_zeros_unz will return 63, because there are 63 zeros before that final 1. But I want the index relative to the 0th bit. I actually need stdc_trailing_zeros_unz.

It's a common slip-up. Just remember: "Leading" is the big end (MSB), "Trailing" is the small end (LSB).

Correcting the index lookup

Let's fix that and actually mark the slot as taken by flipping the bit to 0.

void allocate_slot(slot_mask_t *mask) {
    if (*mask == 0) {
        printf("No slots available!\n");
        return;
    }

    // Find the index of the first '1' bit starting from the LSB
    int slot = stdc_trailing_zeros_unz(*mask);
    
    printf("Allocating slot: %d\n", slot);

    // Clear the bit to mark it as occupied
    *mask &= ~(1ULL << slot);
}

Counting the remaining capacity

Finally, it's often useful to know exactly how many resources we have left. Instead of looping through every bit and incrementing a counter—which is slow—we can use stdc_count_ones_unz. This maps directly to the POPCNT instruction on modern CPUs.

void print_status(slot_mask_t mask) {
    int free_count = stdc_count_ones_unz(mask);
    printf("Current capacity: %d/64 slots free.\n", free_count);
}

int main() {
    // Start with slots 0, 2, and 5 free (binary ...00100101)
    slot_mask_t my_resources = (1ULL << 0) | (1ULL << 2) | (1ULL << 5);

    print_status(my_resources);
    allocate_slot(&my_resources); // Should take slot 0
    print_status(my_resources);
    allocate_slot(&my_resources); // Should take slot 2
    print_status(my_resources);

    return 0;
}

By using stdbit.h, we've replaced manual loops and fragile compiler-specific hacks with a clean, standardized API that the compiler can optimize into a single CPU instruction.




📋 Practical Task

Implement a Bitmask-Based Permission Auditor

You are building a security module where permissions are stored in a uint32_t bitmask. Each bit represents a specific privilege (e.g., Read, Write, Execute, Admin, etc.).

Write a program that does the following:

  • Defines a uint32_t variable representing a user's permissions.
  • Uses stdc_count_ones_unz to print the total number of privileges the user possesses.
  • Uses stdc_trailing_zeros_unz to identify the index of the lowest-level privilege the user has.
  • Includes a check to handle the case where the user has zero permissions (to avoid passing 0 to the stdbit macros, which is undefined behavior).
Rating
0 0

There are no comments for now.

to be the first to leave a comment.