mirror of
https://github.com/nicbarker/clay.git
synced 2025-12-23 17:41:06 +00:00
Complete learning guide covering ALL C concepts from beginner to advanced: Main Guide (LEARNING_C_WITH_CLAY.md): - Added Chapters 3-5: Operators, Control Flow, Loops - Enhanced Chapters 6-13: Functions, Pointers, Structs, Arrays, Strings, Type Casting, Storage Classes, Recursion - All chapters include extensive Clay library examples - Progressive difficulty from basics to advanced topics Specialized Tutorial Files (docs/): - 14_bit_manipulation.md: Comprehensive bit operations guide * Bitwise operators, masks, flags * Counting bits, power of 2 operations * Practical applications with Clay examples - 21_standard_library.md: C Standard Library overview * stdio.h, stdlib.h, string.h, math.h * Clay's zero-dependency approach * Custom implementations vs stdlib - 22_file_io.md: Complete File I/O guide * Text and binary file operations * Configuration files for Clay apps * Error handling and best practices - 23_command_line_arguments.md: CLI argument parsing * argc/argv basics * Flag parsing and subcommands * Clay application configuration examples - README_C_TUTORIALS.md: Master index and learning guide * Complete chapter overview * Learning path recommendations * Progress tracking checklist * Prerequisites and setup instructions Features: - 24 comprehensive chapters covering all C concepts - 100+ code examples with detailed explanations - Real-world patterns from Clay library throughout - Practice exercises for each chapter - Modular organization for easy navigation - Zero-dependency programming concepts - Professional C development practices Total content: ~70,000+ words of detailed tutorials
12 KiB
12 KiB
Chapter 14: Bit Manipulation in C
Complete Guide with Clay Library Examples
14.1 Binary Number System
Understanding binary is essential for bit manipulation:
// Decimal vs Binary
0 = 0000
1 = 0001
2 = 0010
3 = 0011
4 = 0100
5 = 0101
6 = 0110
7 = 0111
8 = 1000
Converting Binary to Decimal
#include <stdio.h>
int binaryToDecimal(int binary) {
int decimal = 0, base = 1;
while (binary > 0) {
int lastDigit = binary % 10;
decimal += lastDigit * base;
base *= 2;
binary /= 10;
}
return decimal;
}
int main(void) {
printf("%d\n", binaryToDecimal(1010)); // 10
printf("%d\n", binaryToDecimal(1111)); // 15
return 0;
}
14.2 Bitwise Operators
AND Operator (&)
// Both bits must be 1
int a = 5; // 0101
int b = 3; // 0011
int c = a & b; // 0001 = 1
// Check if number is even
int isEven(int n) {
return (n & 1) == 0; // Check if last bit is 0
}
Clay Example:
// Check if corner radius flag is set
typedef enum {
CLAY_CORNER_RADIUS_TOP_LEFT = 1 << 0, // 0001
CLAY_CORNER_RADIUS_TOP_RIGHT = 1 << 1, // 0010
CLAY_CORNER_RADIUS_BOTTOM_LEFT = 1 << 2, // 0100
CLAY_CORNER_RADIUS_BOTTOM_RIGHT = 1 << 3 // 1000
} Clay_CornerRadiusSet;
// Check if top-left corner has radius
if (flags & CLAY_CORNER_RADIUS_TOP_LEFT) {
// Top left corner has radius
}
OR Operator (|)
// At least one bit must be 1
int a = 5; // 0101
int b = 3; // 0011
int c = a | b; // 0111 = 7
// Set specific bit
int setBit(int num, int pos) {
return num | (1 << pos);
}
Clay Example:
// Combine multiple corner radius flags
int flags = CLAY_CORNER_RADIUS_TOP_LEFT |
CLAY_CORNER_RADIUS_TOP_RIGHT; // 0011 = both top corners
// Add another flag
flags |= CLAY_CORNER_RADIUS_BOTTOM_LEFT; // Now 0111 = three corners
XOR Operator (^)
// Bits must be different
int a = 5; // 0101
int b = 3; // 0011
int c = a ^ b; // 0110 = 6
// Swap two numbers without temp variable
void swap(int *a, int *b) {
*a = *a ^ *b;
*b = *a ^ *b;
*a = *a ^ *b;
}
NOT Operator (~)
// Inverts all bits
int a = 5; // 00000101
int b = ~a; // 11111010 (in 8-bit)
// Clear specific bit
int clearBit(int num, int pos) {
return num & ~(1 << pos);
}
Left Shift (<<)
// Multiply by 2^n
int a = 5; // 0101
int b = a << 1; // 1010 = 10 (multiply by 2)
int c = a << 2; // 10100 = 20 (multiply by 4)
// Fast power of 2
int powerOf2(int n) {
return 1 << n; // 2^n
}
Clay Example:
// Define bit flags with left shift
typedef enum {
CLAY_CORNER_RADIUS_TOP_LEFT = 1 << 0, // 1
CLAY_CORNER_RADIUS_TOP_RIGHT = 1 << 1, // 2
CLAY_CORNER_RADIUS_BOTTOM_LEFT = 1 << 2, // 4
CLAY_CORNER_RADIUS_BOTTOM_RIGHT = 1 << 3 // 8
} Clay_CornerRadiusSet;
Right Shift (>>)
// Divide by 2^n
int a = 20; // 10100
int b = a >> 1; // 01010 = 10 (divide by 2)
int c = a >> 2; // 00101 = 5 (divide by 4)
// Extract specific bits
int getBits(int num, int start, int count) {
return (num >> start) & ((1 << count) - 1);
}
14.3 Common Bit Operations
Check if Bit is Set
int isBitSet(int num, int pos) {
return (num & (1 << pos)) != 0;
}
int main(void) {
int num = 5; // 0101
printf("%d\n", isBitSet(num, 0)); // 1 (bit 0 is set)
printf("%d\n", isBitSet(num, 1)); // 0 (bit 1 is not set)
printf("%d\n", isBitSet(num, 2)); // 1 (bit 2 is set)
return 0;
}
Set a Bit
int setBit(int num, int pos) {
return num | (1 << pos);
}
int main(void) {
int num = 5; // 0101
num = setBit(num, 1); // 0111 = 7
printf("%d\n", num);
return 0;
}
Clear a Bit
int clearBit(int num, int pos) {
return num & ~(1 << pos);
}
int main(void) {
int num = 7; // 0111
num = clearBit(num, 1); // 0101 = 5
printf("%d\n", num);
return 0;
}
Toggle a Bit
int toggleBit(int num, int pos) {
return num ^ (1 << pos);
}
int main(void) {
int num = 5; // 0101
num = toggleBit(num, 1); // 0111 = 7
num = toggleBit(num, 1); // 0101 = 5 (back to original)
printf("%d\n", num);
return 0;
}
14.4 Bit Masks
Masks select specific bits:
// Extract RGB components from 32-bit color
typedef struct {
uint32_t color; // 0xAARRGGBB
} Color;
uint8_t getRed(Color c) {
return (c.color >> 16) & 0xFF; // Mask: 0x00FF0000
}
uint8_t getGreen(Color c) {
return (c.color >> 8) & 0xFF; // Mask: 0x0000FF00
}
uint8_t getBlue(Color c) {
return c.color & 0xFF; // Mask: 0x000000FF
}
uint8_t getAlpha(Color c) {
return (c.color >> 24) & 0xFF; // Mask: 0xFF000000
}
uint32_t makeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
return ((uint32_t)a << 24) |
((uint32_t)r << 16) |
((uint32_t)g << 8) |
(uint32_t)b;
}
Clay Example (conceptual):
// Clay uses float colors, but shows similar concepts
typedef struct {
float r, g, b, a;
} Clay_Color;
// If Clay used packed colors:
uint32_t packColor(Clay_Color c) {
uint8_t r = (uint8_t)(c.r * 255);
uint8_t g = (uint8_t)(c.g * 255);
uint8_t b = (uint8_t)(c.b * 255);
uint8_t a = (uint8_t)(c.a * 255);
return (a << 24) | (r << 16) | (g << 8) | b;
}
14.5 Bit Fields in Structs
Pack multiple values efficiently:
struct Flags {
unsigned int isVisible : 1; // 1 bit
unsigned int isEnabled : 1; // 1 bit
unsigned int priority : 3; // 3 bits (0-7)
unsigned int id : 11; // 11 bits
}; // Total: 16 bits = 2 bytes
int main(void) {
struct Flags f = {0};
f.isVisible = 1;
f.isEnabled = 1;
f.priority = 5;
f.id = 1024;
printf("Size: %zu bytes\n", sizeof(f)); // 2 or 4 (compiler dependent)
printf("Priority: %u\n", f.priority);
return 0;
}
Clay Example (conceptual):
// Clay could use bit fields for layout flags
typedef struct {
uint32_t layoutDirection : 1; // 0 = horizontal, 1 = vertical
uint32_t wrapChildren : 1; // Wrap to next line
uint32_t alignmentX : 2; // 0-3 for alignment options
uint32_t alignmentY : 2;
uint32_t reserved : 26; // Future use
} Clay_LayoutFlags;
14.6 Counting Set Bits
Method 1: Loop
int countSetBits(int n) {
int count = 0;
while (n > 0) {
count += n & 1; // Add last bit
n >>= 1; // Shift right
}
return count;
}
Method 2: Brian Kernighan's Algorithm
int countSetBits(int n) {
int count = 0;
while (n > 0) {
n &= (n - 1); // Clear rightmost set bit
count++;
}
return count;
}
// Example: n = 12 (1100)
// 1100 & 1011 = 1000 (count = 1)
// 1000 & 0111 = 0000 (count = 2)
Method 3: Lookup Table
// Precomputed table for 4-bit numbers
int table[16] = {
0, 1, 1, 2, 1, 2, 2, 3,
1, 2, 2, 3, 2, 3, 3, 4
};
int countSetBits(int n) {
int count = 0;
while (n > 0) {
count += table[n & 0xF]; // Count 4 bits at a time
n >>= 4;
}
return count;
}
14.7 Power of Two
Check if Power of 2
int isPowerOfTwo(int n) {
return n > 0 && (n & (n - 1)) == 0;
}
// Examples:
// 4 (100) & 3 (011) = 0 → true
// 5 (101) & 4 (100) = 4 → false
// 8 (1000) & 7 (0111) = 0 → true
Next Power of 2
int nextPowerOfTwo(int n) {
if (n <= 0) return 1;
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
return n + 1;
}
int main(void) {
printf("%d\n", nextPowerOfTwo(5)); // 8
printf("%d\n", nextPowerOfTwo(17)); // 32
return 0;
}
14.8 Swap Values
Without Temporary Variable
void swap(int *a, int *b) {
if (a != b) { // Must check for same address
*a ^= *b;
*b ^= *a;
*a ^= *b;
}
}
Swap Nibbles (4-bit)
int swapNibbles(int n) {
return ((n & 0x0F) << 4) | ((n & 0xF0) >> 4);
}
// Example: 0x5A → 0xA5
14.9 Reverse Bits
uint32_t reverseBits(uint32_t n) {
uint32_t result = 0;
for (int i = 0; i < 32; i++) {
result <<= 1; // Shift result left
result |= (n & 1); // Add rightmost bit of n
n >>= 1; // Shift n right
}
return result;
}
14.10 Parity
Check if number of set bits is even or odd:
int getParity(int n) {
int parity = 0;
while (n > 0) {
parity ^= (n & 1);
n >>= 1;
}
return parity; // 0 = even, 1 = odd
}
14.11 Gray Code
Convert between binary and Gray code:
// Binary to Gray
int binaryToGray(int n) {
return n ^ (n >> 1);
}
// Gray to Binary
int grayToBinary(int n) {
int binary = 0;
while (n > 0) {
binary ^= n;
n >>= 1;
}
return binary;
}
14.12 Position of Rightmost Set Bit
int rightmostSetBit(int n) {
return n & ~(n - 1);
}
// Example: 12 (1100)
// Returns: 4 (0100) - position of rightmost 1
14.13 Toggle All Bits After Rightmost Set Bit
int toggleAfterRightmost(int n) {
return n ^ (n - 1);
}
// Example: 12 (1100)
// Returns: 15 (1111)
14.14 Extract and Set Bit Ranges
// Extract bits from position p to p+n
int extractBits(int num, int p, int n) {
return (num >> p) & ((1 << n) - 1);
}
// Set bits from position p to p+n
int setBits(int num, int p, int n, int value) {
int mask = ((1 << n) - 1) << p;
return (num & ~mask) | ((value << p) & mask);
}
14.15 Practical Applications in Clay
Bit Flags for UI State
typedef enum {
CLAY_ELEMENT_VISIBLE = 1 << 0, // 0x01
CLAY_ELEMENT_ENABLED = 1 << 1, // 0x02
CLAY_ELEMENT_FOCUSED = 1 << 2, // 0x04
CLAY_ELEMENT_HOVERED = 1 << 3, // 0x08
CLAY_ELEMENT_PRESSED = 1 << 4, // 0x10
CLAY_ELEMENT_SELECTED = 1 << 5, // 0x20
CLAY_ELEMENT_DISABLED = 1 << 6, // 0x40
CLAY_ELEMENT_HIDDEN = 1 << 7 // 0x80
} Clay_ElementFlags;
typedef struct {
uint32_t flags;
} Clay_Element;
// Check state
int isVisible(Clay_Element *el) {
return (el->flags & CLAY_ELEMENT_VISIBLE) != 0;
}
// Set state
void setVisible(Clay_Element *el, int visible) {
if (visible) {
el->flags |= CLAY_ELEMENT_VISIBLE; // Set bit
} else {
el->flags &= ~CLAY_ELEMENT_VISIBLE; // Clear bit
}
}
// Toggle state
void toggleVisible(Clay_Element *el) {
el->flags ^= CLAY_ELEMENT_VISIBLE;
}
// Multiple states at once
void setInteractive(Clay_Element *el) {
el->flags |= (CLAY_ELEMENT_VISIBLE |
CLAY_ELEMENT_ENABLED);
}
Optimized Hash Functions
// Clay uses hashing for element lookup
uint32_t hashString(const char *str, size_t length) {
uint32_t hash = 0;
for (size_t i = 0; i < length; i++) {
hash = (hash << 5) - hash + str[i]; // hash * 31 + c
// Using bit shift instead of multiply
}
return hash;
}
Memory Alignment
// Align size to power of 2
size_t alignSize(size_t size, size_t alignment) {
return (size + alignment - 1) & ~(alignment - 1);
}
// Example: align to 16 bytes
// size = 25
// (25 + 15) & ~15 = 40 & 0xFFFFFFF0 = 32
14.16 Performance Tips
Use bit operations for:
- ✅ Powers of 2 multiplication/division
- ✅ Flag checks (faster than booleans)
- ✅ Color packing/unpacking
- ✅ Hash functions
- ✅ Alignment calculations
Avoid for:
- ❌ Regular arithmetic (compiler optimizes)
- ❌ Readability-critical code
- ❌ Floating-point operations
14.17 Key Concepts Learned
- ✅ Bitwise operators: &, |, ^, ~, <<, >>
- ✅ Bit manipulation: set, clear, toggle, check
- ✅ Bit masks and extraction
- ✅ Bit fields in structs
- ✅ Counting set bits
- ✅ Power of 2 operations
- ✅ Practical applications in Clay
- ✅ Performance considerations
Practice Exercises
- Write a function to count trailing zeros in a number
- Implement bitwise rotation (rotate left/right)
- Find the only non-duplicate number in an array (all others appear twice)
- Swap all odd and even bits in a number
- Add two numbers without using + operator
- Implement a bit vector data structure
- Create a compact permission system using bit flags
- Write efficient popcount for 64-bit numbers