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
662 lines
12 KiB
Markdown
662 lines
12 KiB
Markdown
# Chapter 14: Bit Manipulation in C
|
|
|
|
## Complete Guide with Clay Library Examples
|
|
|
|
---
|
|
|
|
## 14.1 Binary Number System
|
|
|
|
Understanding binary is essential for bit manipulation:
|
|
|
|
```c
|
|
// 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
|
|
|
|
```c
|
|
#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 (&)
|
|
|
|
```c
|
|
// 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:**
|
|
```c
|
|
// 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 (|)
|
|
|
|
```c
|
|
// 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:**
|
|
```c
|
|
// 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 (^)
|
|
|
|
```c
|
|
// 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 (~)
|
|
|
|
```c
|
|
// 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 (<<)
|
|
|
|
```c
|
|
// 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:**
|
|
```c
|
|
// 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 (>>)
|
|
|
|
```c
|
|
// 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
|
|
|
|
```c
|
|
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
|
|
|
|
```c
|
|
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
|
|
|
|
```c
|
|
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
|
|
|
|
```c
|
|
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:
|
|
|
|
```c
|
|
// 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):**
|
|
```c
|
|
// 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:
|
|
|
|
```c
|
|
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):**
|
|
```c
|
|
// 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
|
|
|
|
```c
|
|
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
|
|
|
|
```c
|
|
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
|
|
|
|
```c
|
|
// 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
|
|
|
|
```c
|
|
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
|
|
|
|
```c
|
|
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
|
|
|
|
```c
|
|
void swap(int *a, int *b) {
|
|
if (a != b) { // Must check for same address
|
|
*a ^= *b;
|
|
*b ^= *a;
|
|
*a ^= *b;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Swap Nibbles (4-bit)
|
|
|
|
```c
|
|
int swapNibbles(int n) {
|
|
return ((n & 0x0F) << 4) | ((n & 0xF0) >> 4);
|
|
}
|
|
|
|
// Example: 0x5A → 0xA5
|
|
```
|
|
|
|
---
|
|
|
|
## 14.9 Reverse Bits
|
|
|
|
```c
|
|
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:
|
|
|
|
```c
|
|
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:
|
|
|
|
```c
|
|
// 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
|
|
|
|
```c
|
|
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
|
|
|
|
```c
|
|
int toggleAfterRightmost(int n) {
|
|
return n ^ (n - 1);
|
|
}
|
|
|
|
// Example: 12 (1100)
|
|
// Returns: 15 (1111)
|
|
```
|
|
|
|
---
|
|
|
|
## 14.14 Extract and Set Bit Ranges
|
|
|
|
```c
|
|
// 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
|
|
|
|
```c
|
|
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
|
|
|
|
```c
|
|
// 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
|
|
|
|
```c
|
|
// 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
|
|
|
|
1. Write a function to count trailing zeros in a number
|
|
2. Implement bitwise rotation (rotate left/right)
|
|
3. Find the only non-duplicate number in an array (all others appear twice)
|
|
4. Swap all odd and even bits in a number
|
|
5. Add two numbers without using + operator
|
|
6. Implement a bit vector data structure
|
|
7. Create a compact permission system using bit flags
|
|
8. Write efficient popcount for 64-bit numbers
|