mirror of
https://github.com/nicbarker/clay.git
synced 2025-12-23 09:41:04 +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
14 KiB
14 KiB
Chapter 21: C Standard Library Basics
Complete Guide with Clay Library Examples
21.1 What is the C Standard Library?
The C Standard Library provides essential functions for:
- Input/Output (
stdio.h) - String manipulation (
string.h) - Memory management (
stdlib.h) - Math operations (
math.h) - Character handling (
ctype.h) - Time/Date (
time.h)
Clay's Approach: Clay is zero-dependency - it doesn't use the standard library! This shows how to write portable C without stdlib.
21.2 stdio.h - Input/Output
Printf Family
#include <stdio.h>
int main(void) {
// Basic printf
printf("Hello, World!\n");
// Format specifiers
int age = 25;
float height = 5.9f;
char grade = 'A';
printf("Age: %d\n", age); // int
printf("Height: %.2f\n", height); // float with 2 decimals
printf("Grade: %c\n", grade); // char
printf("Hex: %x\n", 255); // hexadecimal (ff)
printf("Pointer: %p\n", (void*)&age); // pointer address
// Width and padding
printf("%5d\n", 42); // " 42" (right-aligned)
printf("%-5d\n", 42); // "42 " (left-aligned)
printf("%05d\n", 42); // "00042" (zero-padded)
return 0;
}
Sprintf - Format to String
#include <stdio.h>
int main(void) {
char buffer[100];
int x = 10, y = 20;
sprintf(buffer, "Point: (%d, %d)", x, y);
printf("%s\n", buffer); // "Point: (10, 20)"
// Safer version with size limit
snprintf(buffer, sizeof(buffer), "Value: %d", 42);
return 0;
}
Scanf Family
#include <stdio.h>
int main(void) {
int age;
float height;
char name[50];
printf("Enter age: ");
scanf("%d", &age); // Note: address-of operator!
printf("Enter height: ");
scanf("%f", &height);
printf("Enter name: ");
scanf("%49s", name); // Limit input to avoid overflow
printf("Name: %s, Age: %d, Height: %.1f\n", name, age, height);
return 0;
}
File Operations
#include <stdio.h>
int main(void) {
FILE *file;
// Write to file
file = fopen("output.txt", "w");
if (file == NULL) {
printf("Error opening file!\n");
return 1;
}
fprintf(file, "Hello, File!\n");
fclose(file);
// Read from file
file = fopen("output.txt", "r");
if (file != NULL) {
char buffer[100];
while (fgets(buffer, sizeof(buffer), file) != NULL) {
printf("%s", buffer);
}
fclose(file);
}
return 0;
}
Clay doesn't use stdio.h - it's renderer-agnostic and doesn't handle file I/O internally.
21.3 stdlib.h - General Utilities
Memory Allocation
#include <stdlib.h>
int main(void) {
// Allocate memory
int *arr = (int*)malloc(10 * sizeof(int));
if (arr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// Use array
for (int i = 0; i < 10; i++) {
arr[i] = i * 2;
}
// Resize array
arr = (int*)realloc(arr, 20 * sizeof(int));
// Free memory
free(arr);
return 0;
}
Clay's Approach: Uses custom arena allocators instead of malloc/free!
// Clay's arena allocator (from clay.h)
typedef struct {
size_t capacity;
size_t nextAllocation;
char *memory;
} Clay_Arena;
void* Clay__AllocateArena(Clay_Arena *arena, size_t size) {
if (arena->nextAllocation + size <= arena->capacity) {
void *ptr = arena->memory + arena->nextAllocation;
arena->nextAllocation += size;
return ptr;
}
return NULL; // Out of memory
}
String to Number Conversion
#include <stdlib.h>
int main(void) {
// String to integer
int num1 = atoi("123"); // 123
long num2 = atol("123456789"); // 123456789
long long num3 = atoll("9999999999"); // 9999999999
// String to float
double num4 = atof("3.14159"); // 3.14159
// More robust conversion with error checking
char *endptr;
long value = strtol("123abc", &endptr, 10);
if (*endptr != '\0') {
printf("Invalid number: stopped at '%s'\n", endptr);
}
return 0;
}
Random Numbers
#include <stdlib.h>
#include <time.h>
int main(void) {
// Seed random number generator
srand(time(NULL));
// Generate random numbers
for (int i = 0; i < 5; i++) {
int random = rand(); // 0 to RAND_MAX
printf("%d\n", random);
}
// Random in range [min, max]
int min = 1, max = 100;
int randomInRange = min + rand() % (max - min + 1);
printf("Random (1-100): %d\n", randomInRange);
return 0;
}
Program Termination
#include <stdlib.h>
void cleanup(void) {
printf("Cleaning up...\n");
}
int main(void) {
// Register cleanup function
atexit(cleanup);
// Normal exit
// exit(0); // Success
// exit(1); // Failure
// Abnormal termination
// abort(); // Immediate termination
return 0;
} // cleanup() called automatically
Environment Variables
#include <stdlib.h>
#include <stdio.h>
int main(void) {
// Get environment variable
char *path = getenv("PATH");
if (path != NULL) {
printf("PATH: %s\n", path);
}
char *home = getenv("HOME");
if (home != NULL) {
printf("HOME: %s\n", home);
}
return 0;
}
21.4 string.h - String Manipulation
String Length
#include <string.h>
int main(void) {
char str[] = "Hello";
size_t len = strlen(str); // 5
printf("Length: %zu\n", len);
return 0;
}
Clay's Implementation (from clay.h):
// Clay uses explicit length, no strlen needed
typedef struct {
int32_t length; // Length stored directly!
const char *chars;
} Clay_String;
// O(1) length access
int getLength(Clay_String *str) {
return str->length; // No need to count!
}
String Copy
#include <string.h>
int main(void) {
char src[] = "Hello";
char dest[20];
// Copy string
strcpy(dest, src);
// Copy with size limit (safer)
strncpy(dest, src, sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0'; // Ensure null termination
printf("%s\n", dest);
return 0;
}
String Concatenation
#include <string.h>
int main(void) {
char str[50] = "Hello";
// Concatenate
strcat(str, " World"); // "Hello World"
// Concatenate with limit (safer)
strncat(str, "!", sizeof(str) - strlen(str) - 1);
printf("%s\n", str);
return 0;
}
String Comparison
#include <string.h>
int main(void) {
char str1[] = "Apple";
char str2[] = "Banana";
// Compare strings
int result = strcmp(str1, str2);
if (result < 0) {
printf("%s comes before %s\n", str1, str2);
} else if (result > 0) {
printf("%s comes after %s\n", str1, str2);
} else {
printf("Strings are equal\n");
}
// Compare n characters
if (strncmp(str1, str2, 3) == 0) {
printf("First 3 characters match\n");
}
return 0;
}
Memory Operations
#include <string.h>
int main(void) {
char buffer[20];
// Set memory to value
memset(buffer, 'A', 10);
buffer[10] = '\0';
printf("%s\n", buffer); // "AAAAAAAAAA"
// Copy memory
char src[] = "Hello";
char dest[20];
memcpy(dest, src, strlen(src) + 1); // Include \0
// Move memory (handles overlapping regions)
memmove(dest + 2, dest, strlen(dest) + 1); // Shift right
printf("%s\n", dest); // "HeHello"
// Compare memory
if (memcmp(src, dest, 5) == 0) {
printf("Memory regions match\n");
}
return 0;
}
Clay's Approach:
// Clay uses custom memory functions when needed
static inline void Clay__MemoryCopy(
void *dest,
const void *src,
size_t size
) {
char *d = (char*)dest;
const char *s = (const char*)src;
for (size_t i = 0; i < size; i++) {
d[i] = s[i];
}
}
21.5 math.h - Mathematical Functions
#include <math.h>
#include <stdio.h>
int main(void) {
// Power and roots
printf("2^3 = %.0f\n", pow(2, 3)); // 8
printf("sqrt(16) = %.0f\n", sqrt(16)); // 4
printf("cbrt(27) = %.0f\n", cbrt(27)); // 3
// Trigonometry
printf("sin(π/2) = %.1f\n", sin(M_PI / 2)); // 1.0
printf("cos(0) = %.1f\n", cos(0)); // 1.0
printf("tan(π/4) = %.1f\n", tan(M_PI / 4)); // 1.0
// Rounding
printf("ceil(3.2) = %.0f\n", ceil(3.2)); // 4
printf("floor(3.8) = %.0f\n", floor(3.8)); // 3
printf("round(3.5) = %.0f\n", round(3.5)); // 4
// Absolute value
printf("fabs(-5.5) = %.1f\n", fabs(-5.5)); // 5.5
printf("abs(-5) = %d\n", abs(-5)); // 5
// Logarithms
printf("log(e) = %.1f\n", log(M_E)); // 1.0
printf("log10(100) = %.0f\n", log10(100)); // 2
// Exponential
printf("exp(1) = %.2f\n", exp(1)); // 2.72 (e)
return 0;
}
Clay's Implementation:
// Clay implements its own min/max (no stdlib needed)
static inline float Clay__Min(float a, float b) {
return a < b ? a : b;
}
static inline float Clay__Max(float a, float b) {
return a > b ? a : b;
}
static inline float Clay__Clamp(float value, float min, float max) {
return Clay__Max(min, Clay__Min(value, max));
}
21.6 ctype.h - Character Handling
#include <ctype.h>
#include <stdio.h>
int main(void) {
char ch = 'A';
// Character testing
if (isalpha(ch)) printf("Letter\n");
if (isdigit(ch)) printf("Digit\n");
if (isalnum(ch)) printf("Alphanumeric\n");
if (isupper(ch)) printf("Uppercase\n");
if (islower(ch)) printf("Lowercase\n");
if (isspace(ch)) printf("Whitespace\n");
if (ispunct(ch)) printf("Punctuation\n");
// Character conversion
printf("Upper: %c\n", toupper('a')); // 'A'
printf("Lower: %c\n", tolower('A')); // 'a'
// Practical example: validate identifier
int isValidIdentifier(const char *str) {
if (!isalpha(str[0]) && str[0] != '_') {
return 0; // Must start with letter or _
}
for (int i = 1; str[i] != '\0'; i++) {
if (!isalnum(str[i]) && str[i] != '_') {
return 0;
}
}
return 1;
}
printf("valid_name: %d\n", isValidIdentifier("valid_name")); // 1
printf("123invalid: %d\n", isValidIdentifier("123invalid")); // 0
return 0;
}
21.7 time.h - Time and Date
#include <time.h>
#include <stdio.h>
int main(void) {
// Current time
time_t now = time(NULL);
printf("Seconds since epoch: %ld\n", now);
// Convert to readable format
struct tm *timeinfo = localtime(&now);
printf("Current time: %s", asctime(timeinfo));
// Format time
char buffer[80];
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", timeinfo);
printf("Formatted: %s\n", buffer);
// Measure execution time
clock_t start = clock();
// Do some work
for (int i = 0; i < 1000000; i++) {
// Busy work
}
clock_t end = clock();
double cpu_time = ((double)(end - start)) / CLOCKS_PER_SEC;
printf("CPU time: %.6f seconds\n", cpu_time);
// Sleep (POSIX)
// sleep(1); // Sleep 1 second
return 0;
}
21.8 assert.h - Debugging
#include <assert.h>
#include <stdio.h>
int divide(int a, int b) {
assert(b != 0); // Crash if b is 0 (in debug builds)
return a / b;
}
int main(void) {
int result = divide(10, 2); // OK
printf("Result: %d\n", result);
// result = divide(10, 0); // Crashes with error message
// Disable asserts in release
// Compile with: gcc -DNDEBUG program.c
return 0;
}
Clay's Approach:
// Clay has custom error handling
#define CLAY__ASSERT(condition) \
if (!(condition)) { \
Clay__ErrorHandler(...); \
}
21.9 stdint.h - Fixed-Width Integers
#include <stdint.h>
#include <stdio.h>
int main(void) {
// Exact-width integers
int8_t i8 = 127; // -128 to 127
uint8_t u8 = 255; // 0 to 255
int16_t i16 = 32767; // -32768 to 32767
uint16_t u16 = 65535; // 0 to 65535
int32_t i32 = 2147483647;
uint32_t u32 = 4294967295;
int64_t i64 = 9223372036854775807;
uint64_t u64 = 18446744073709551615ULL;
// Pointer-sized integers
intptr_t iptr; // Can hold pointer
uintptr_t uptr;
// Fast types (at least N bits)
int_fast32_t fast32; // Fast 32-bit (may be 64-bit)
int_least32_t least32; // At least 32-bit (may be 32+)
// Print with proper format specifiers
printf("int32: %" PRId32 "\n", i32);
printf("uint64: %" PRIu64 "\n", u64);
return 0;
}
Clay uses fixed-width types extensively:
typedef struct {
int32_t capacity; // Exactly 32 bits
int32_t length;
uint32_t *internalArray;
} Clay__int32_tArray;
21.10 stdbool.h - Boolean Type
#include <stdbool.h>
#include <stdio.h>
int main(void) {
bool isActive = true;
bool isValid = false;
if (isActive) {
printf("Active!\n");
}
// Boolean operations
bool result = isActive && !isValid; // true
return 0;
}
Pre-C99 approach:
typedef enum {
false = 0,
true = 1
} bool;
21.11 limits.h - Implementation Limits
#include <limits.h>
#include <stdio.h>
int main(void) {
printf("CHAR_BIT: %d\n", CHAR_BIT); // 8
printf("CHAR_MIN: %d\n", CHAR_MIN);
printf("CHAR_MAX: %d\n", CHAR_MAX);
printf("INT_MIN: %d\n", INT_MIN); // -2147483648
printf("INT_MAX: %d\n", INT_MAX); // 2147483647
printf("LONG_MAX: %ld\n", LONG_MAX);
// Check for overflow
int x = INT_MAX;
if (x + 1 < x) {
printf("Overflow detected!\n");
}
return 0;
}
21.12 Key Concepts Learned
- ✅ stdio.h for I/O operations
- ✅ stdlib.h for memory and utilities
- ✅ string.h for string manipulation
- ✅ math.h for mathematical functions
- ✅ ctype.h for character handling
- ✅ time.h for time/date operations
- ✅ assert.h for debugging
- ✅ stdint.h for fixed-width types
- ✅ stdbool.h for boolean type
- ✅ Clay's zero-dependency approach
Practice Exercises
- Write a program to read a file and count words
- Implement your own strlen without using stdlib
- Create a simple calculator using scanf and math.h
- Build a random password generator
- Make a function to format time as "X days, Y hours, Z minutes"
- Implement case-insensitive string comparison
- Create a memory pool allocator (like Clay's arena)
- Write a benchmark utility using clock()