clay/docs/21_standard_library.md
Claude faea55a9b9
Add comprehensive C learning materials with specialized topic tutorials
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
2025-11-13 20:32:16 +00:00

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

  1. Write a program to read a file and count words
  2. Implement your own strlen without using stdlib
  3. Create a simple calculator using scanf and math.h
  4. Build a random password generator
  5. Make a function to format time as "X days, Y hours, Z minutes"
  6. Implement case-insensitive string comparison
  7. Create a memory pool allocator (like Clay's arena)
  8. Write a benchmark utility using clock()