C
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Arrays and Strings
-
Section 5: Pointers
-
Section 6: Memory Management
-
Section 7: Structures and Unions
-
Section 8: The Preprocessor and Build Process
-
Section 9: Standard Library: stdio.h
-
Section 10: Standard Library: stdlib.h
-
Section 11: Standard Library: string.h
-
Section 12: Standard Library: ctype.h and wctype.h
-
Section 13: Standard Library: math.h, complex.h, fenv.h, tgmath.h
-
Section 14: Standard Library: Type and Limit Headers
-
Section 15: Standard Library: Error Handling and Debugging
-
Section 16: Standard Library: Localization and Encoding
-
Section 17: Standard Library: time.h
-
Section 18: Standard Library: Concurrency (C11)
-
Section 19: POSIX and System Programming (unistd.h)
-
Section 20: More Data Structures
-
Section 21: Algorithms in C
-
Section 22: Bitwise Operations
-
Section 23: Command-Line Programs
-
Section 24: Debugging and Best Practices
-
Section 25: Compiler and Language Internals
-
Section 26: Embedded and Cross-Platform Considerations
-
Section 27: Networking Basics
-
Section 28: Practical Projects
-
Section 29: Interview Practice
-
Section 30: C23 Modern Features
-
Section 31: More Practice and Review
118: Bit Utilities with stdbit.h (C23)
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_tvariable representing a user's permissions. - Uses
stdc_count_ones_unzto print the total number of privileges the user possesses. - Uses
stdc_trailing_zeros_unzto 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
stdbitmacros, which is undefined behavior).
There are no comments for now.