mirror of
https://github.com/nicbarker/clay.git
synced 2025-12-23 17:41:06 +00:00
- Complete step-by-step tutorial covering all major C concepts - 13 chapters from basics to advanced topics - Real-world examples from Clay library throughout - Topics include: variables, functions, pointers, structs, arrays, memory management, macros, header files, enums, unions, and more - Practical exercises and next steps for learners
2640 lines
51 KiB
Markdown
2640 lines
51 KiB
Markdown
# Learning C Programming with Clay: A Complete Step-by-Step Guide
|
|
|
|
## Table of Contents
|
|
1. [Introduction to C and Clay](#introduction)
|
|
2. [Chapter 1: C Basics - Your First Program](#chapter-1)
|
|
3. [Chapter 2: Variables and Data Types](#chapter-2)
|
|
4. [Chapter 3: Functions](#chapter-3)
|
|
5. [Chapter 4: Pointers - The Heart of C](#chapter-4)
|
|
6. [Chapter 5: Structs and Typedef](#chapter-5)
|
|
7. [Chapter 6: Arrays and Memory](#chapter-6)
|
|
8. [Chapter 7: Preprocessor and Macros](#chapter-7)
|
|
9. [Chapter 8: Advanced Macros and Metaprogramming](#chapter-8)
|
|
10. [Chapter 9: Memory Management](#chapter-9)
|
|
11. [Chapter 10: Header Files and Project Organization](#chapter-10)
|
|
12. [Chapter 11: Enums and Unions](#chapter-11)
|
|
13. [Chapter 12: Function Pointers and Callbacks](#chapter-12)
|
|
14. [Chapter 13: Building Complete Programs](#chapter-13)
|
|
|
|
---
|
|
|
|
## Introduction to C and Clay {#introduction}
|
|
|
|
### What is C?
|
|
|
|
C is a powerful, low-level programming language created in 1972. It's:
|
|
- **Fast**: Compiles to native machine code
|
|
- **Portable**: Runs on almost any hardware
|
|
- **Simple**: Small set of keywords and features
|
|
- **Powerful**: Direct memory access and hardware control
|
|
- **Foundation**: Many languages (C++, Java, Python internals) are built in C
|
|
|
|
### What is Clay?
|
|
|
|
Clay is a high-performance 2D UI layout library that demonstrates professional C programming:
|
|
- **Single-header library**: Entire implementation in one file
|
|
- **Zero dependencies**: No standard library required
|
|
- **Microsecond performance**: Extremely fast
|
|
- **Production-ready**: Real-world, well-designed code
|
|
|
|
### Why Learn C with Clay?
|
|
|
|
Clay shows you:
|
|
- Professional C code patterns
|
|
- Real-world memory management
|
|
- Advanced macro techniques
|
|
- API design principles
|
|
- Performance optimization
|
|
|
|
Let's begin!
|
|
|
|
---
|
|
|
|
## Chapter 1: C Basics - Your First Program {#chapter-1}
|
|
|
|
### 1.1 The Simplest C Program
|
|
|
|
Every C program starts with a `main` function:
|
|
|
|
```c
|
|
int main(void) {
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Breaking it down:**
|
|
- `int` - The function returns an integer (0 = success)
|
|
- `main` - Special function name (program entry point)
|
|
- `void` - Takes no parameters
|
|
- `return 0` - Exit code (0 means success)
|
|
|
|
### 1.2 Including Headers
|
|
|
|
To use functions from other files, we include headers:
|
|
|
|
```c
|
|
#include <stdio.h> // Standard Input/Output
|
|
|
|
int main(void) {
|
|
printf("Hello, World!\n");
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example:**
|
|
```c
|
|
#define CLAY_IMPLEMENTATION
|
|
#include "clay.h" // Include Clay library
|
|
|
|
int main(void) {
|
|
// Clay initialization code here
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### 1.3 Comments
|
|
|
|
Two types of comments in C:
|
|
|
|
```c
|
|
// Single-line comment
|
|
|
|
/*
|
|
Multi-line comment
|
|
Can span multiple lines
|
|
*/
|
|
```
|
|
|
|
**Clay Example** (from clay.h:1):
|
|
```c
|
|
/*
|
|
Clay 0.12 - A High Performance UI Layout Library in C
|
|
|
|
Features:
|
|
- Flexbox-style responsive layout
|
|
- Single header library
|
|
- Microsecond layout performance
|
|
*/
|
|
```
|
|
|
|
### 1.4 Key Concepts Learned
|
|
- ✅ Basic program structure
|
|
- ✅ The main() function
|
|
- ✅ Including headers with #include
|
|
- ✅ Comments
|
|
|
|
---
|
|
|
|
## Chapter 2: Variables and Data Types {#chapter-2}
|
|
|
|
### 2.1 Basic Data Types
|
|
|
|
C has several built-in types:
|
|
|
|
```c
|
|
int main(void) {
|
|
// Integer types
|
|
int age = 25; // Signed integer (usually 32 bits)
|
|
unsigned int count = 100; // Unsigned (only positive)
|
|
|
|
// Floating-point types
|
|
float pi = 3.14f; // Single precision
|
|
double precise = 3.14159; // Double precision
|
|
|
|
// Character type
|
|
char letter = 'A'; // Single character
|
|
|
|
// Boolean (C99+)
|
|
_Bool isTrue = 1; // 0 = false, 1 = true
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### 2.2 Fixed-Width Integer Types
|
|
|
|
Modern C uses fixed-width types for portability:
|
|
|
|
```c
|
|
#include <stdint.h>
|
|
|
|
int main(void) {
|
|
int8_t small = 127; // 8-bit signed (-128 to 127)
|
|
uint8_t byte = 255; // 8-bit unsigned (0 to 255)
|
|
int16_t medium = 32767; // 16-bit signed
|
|
uint16_t umedium = 65535; // 16-bit unsigned
|
|
int32_t large = 2147483647; // 32-bit signed
|
|
uint32_t ularge = 4294967295; // 32-bit unsigned
|
|
int64_t huge = 9223372036854775807; // 64-bit signed
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
// Clay uses fixed-width types for precision
|
|
typedef struct {
|
|
int32_t capacity; // Exactly 32 bits
|
|
int32_t length;
|
|
uint32_t *internalArray; // Unsigned 32-bit
|
|
} Clay__int32_tArray;
|
|
```
|
|
|
|
### 2.3 Type Sizes
|
|
|
|
```c
|
|
#include <stdio.h>
|
|
|
|
int main(void) {
|
|
printf("int size: %zu bytes\n", sizeof(int));
|
|
printf("float size: %zu bytes\n", sizeof(float));
|
|
printf("double size: %zu bytes\n", sizeof(double));
|
|
printf("char size: %zu bytes\n", sizeof(char));
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### 2.4 Constants
|
|
|
|
Make values unchangeable:
|
|
|
|
```c
|
|
const int MAX_ITEMS = 100; // Cannot be changed
|
|
const float PI = 3.14159f;
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
const Clay_Color CLAY_COLOR_WHITE = {255, 255, 255, 255};
|
|
const Clay_Color CLAY_COLOR_BLACK = {0, 0, 0, 255};
|
|
```
|
|
|
|
### 2.5 Key Concepts Learned
|
|
- ✅ Basic types: int, float, double, char
|
|
- ✅ Fixed-width types: int32_t, uint32_t, etc.
|
|
- ✅ sizeof operator
|
|
- ✅ const keyword
|
|
|
|
---
|
|
|
|
## Chapter 3: Functions {#chapter-3}
|
|
|
|
### 3.1 Function Basics
|
|
|
|
Functions organize code into reusable blocks:
|
|
|
|
```c
|
|
// Function declaration (prototype)
|
|
int add(int a, int b);
|
|
|
|
int main(void) {
|
|
int result = add(5, 3); // Call the function
|
|
return 0;
|
|
}
|
|
|
|
// Function definition
|
|
int add(int a, int b) {
|
|
return a + b;
|
|
}
|
|
```
|
|
|
|
**Parts of a function:**
|
|
- `int` - Return type
|
|
- `add` - Function name
|
|
- `(int a, int b)` - Parameters
|
|
- `{ ... }` - Function body
|
|
|
|
### 3.2 Void Functions
|
|
|
|
Functions that don't return a value:
|
|
|
|
```c
|
|
void printMessage(void) {
|
|
printf("Hello!\n");
|
|
}
|
|
|
|
int main(void) {
|
|
printMessage(); // No return value
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
void Clay_BeginLayout(void) {
|
|
// Starts a new layout frame
|
|
Clay__currentContext->layoutElementsHashMapInternal.length = 0;
|
|
Clay__treeNodeVisited.length = 0;
|
|
// ... more initialization
|
|
}
|
|
```
|
|
|
|
### 3.3 Function with Multiple Parameters
|
|
|
|
```c
|
|
float calculateArea(float width, float height) {
|
|
return width * height;
|
|
}
|
|
|
|
int main(void) {
|
|
float area = calculateArea(10.5f, 5.2f);
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
Clay_Dimensions Clay__MeasureTextCached(
|
|
Clay_String *text,
|
|
Clay_TextElementConfig *config
|
|
) {
|
|
// Custom text measurement function
|
|
Clay_Dimensions dimensions = {0};
|
|
// ... measurement logic
|
|
return dimensions;
|
|
}
|
|
```
|
|
|
|
### 3.4 Static Functions (Internal Linkage)
|
|
|
|
`static` makes functions private to a file:
|
|
|
|
```c
|
|
// Only visible in this file
|
|
static int helperFunction(int x) {
|
|
return x * 2;
|
|
}
|
|
|
|
int publicFunction(int x) {
|
|
return helperFunction(x); // Can use it here
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
// Internal function, not exposed to users
|
|
static inline Clay_BoundingBox Clay__HashMapElementBoundingBox(
|
|
int32_t index
|
|
) {
|
|
// Implementation details hidden from API users
|
|
}
|
|
```
|
|
|
|
### 3.5 Inline Functions
|
|
|
|
`inline` suggests the compiler to insert code directly (faster):
|
|
|
|
```c
|
|
static inline int max(int a, int b) {
|
|
return (a > b) ? a : b;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
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;
|
|
}
|
|
```
|
|
|
|
### 3.6 Key Concepts Learned
|
|
- ✅ Function declaration vs definition
|
|
- ✅ Parameters and return values
|
|
- ✅ void functions
|
|
- ✅ static keyword for internal functions
|
|
- ✅ inline optimization hint
|
|
|
|
---
|
|
|
|
## Chapter 4: Pointers - The Heart of C {#chapter-4}
|
|
|
|
### 4.1 What is a Pointer?
|
|
|
|
A pointer stores a memory address:
|
|
|
|
```c
|
|
int main(void) {
|
|
int age = 25; // Regular variable
|
|
int *ptr = &age; // Pointer to age
|
|
|
|
// ptr holds the address of age
|
|
// *ptr accesses the value at that address
|
|
|
|
printf("age = %d\n", age); // 25
|
|
printf("&age = %p\n", &age); // Address (e.g., 0x7fff5fbff5ac)
|
|
printf("ptr = %p\n", ptr); // Same address
|
|
printf("*ptr = %d\n", *ptr); // 25 (dereferencing)
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Key operators:**
|
|
- `&` - Address-of operator (get address)
|
|
- `*` - Dereference operator (get value at address)
|
|
|
|
### 4.2 Pointer Syntax
|
|
|
|
```c
|
|
int x = 10;
|
|
int *p; // Declare pointer to int
|
|
p = &x; // p now points to x
|
|
*p = 20; // Changes x to 20 through pointer
|
|
|
|
float y = 3.14f;
|
|
float *fp = &y; // Pointer to float
|
|
|
|
char c = 'A';
|
|
char *cp = &c; // Pointer to char
|
|
```
|
|
|
|
### 4.3 Pointers as Function Parameters
|
|
|
|
Pass by reference to modify variables:
|
|
|
|
```c
|
|
// Pass by value (doesn't modify original)
|
|
void incrementValue(int x) {
|
|
x = x + 1; // Only modifies local copy
|
|
}
|
|
|
|
// Pass by pointer (modifies original)
|
|
void incrementPointer(int *x) {
|
|
*x = *x + 1; // Modifies original through pointer
|
|
}
|
|
|
|
int main(void) {
|
|
int num = 5;
|
|
|
|
incrementValue(num);
|
|
printf("%d\n", num); // Still 5
|
|
|
|
incrementPointer(&num);
|
|
printf("%d\n", num); // Now 6
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
// Takes pointer to modify the element
|
|
void Clay__OpenElement(void) {
|
|
Clay_LayoutElement *openLayoutElement =
|
|
Clay__LayoutElementPointerArray_Get(
|
|
&Clay__currentContext->layoutElements,
|
|
Clay__currentContext->layoutElements.length++
|
|
);
|
|
// Modifies element through pointer
|
|
}
|
|
```
|
|
|
|
### 4.4 NULL Pointers
|
|
|
|
A pointer to nothing:
|
|
|
|
```c
|
|
int *p = NULL; // Points to nothing (address 0)
|
|
|
|
if (p == NULL) {
|
|
printf("Pointer is null\n");
|
|
}
|
|
|
|
// Don't dereference NULL pointers!
|
|
// *p = 5; // CRASH!
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
typedef struct {
|
|
Clay_ElementId elementId;
|
|
Clay_LayoutElement *layoutElement; // Can be NULL
|
|
Clay_BoundingBox boundingBox;
|
|
} Clay__LayoutElementTreeNode;
|
|
|
|
// Check before use
|
|
if (layoutElement != NULL) {
|
|
// Safe to use
|
|
}
|
|
```
|
|
|
|
### 4.5 Pointer Arithmetic
|
|
|
|
Pointers can be incremented/decremented:
|
|
|
|
```c
|
|
int arr[5] = {10, 20, 30, 40, 50};
|
|
int *p = arr; // Points to first element
|
|
|
|
printf("%d\n", *p); // 10
|
|
p++; // Move to next element
|
|
printf("%d\n", *p); // 20
|
|
p += 2; // Move 2 elements forward
|
|
printf("%d\n", *p); // 40
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
// Iterate through array with pointer
|
|
char *chars = string.chars;
|
|
for (int i = 0; i < string.length; i++) {
|
|
char current = chars[i]; // or *(chars + i)
|
|
// Process character
|
|
}
|
|
```
|
|
|
|
### 4.6 Key Concepts Learned
|
|
- ✅ Pointers store memory addresses
|
|
- ✅ & (address-of) and * (dereference) operators
|
|
- ✅ Pass by pointer to modify variables
|
|
- ✅ NULL pointers
|
|
- ✅ Pointer arithmetic
|
|
|
|
---
|
|
|
|
## Chapter 5: Structs and Typedef {#chapter-5}
|
|
|
|
### 5.1 Structs - Grouping Related Data
|
|
|
|
Structs group multiple variables together:
|
|
|
|
```c
|
|
struct Person {
|
|
char name[50];
|
|
int age;
|
|
float height;
|
|
};
|
|
|
|
int main(void) {
|
|
struct Person john;
|
|
john.age = 30;
|
|
john.height = 5.9f;
|
|
|
|
printf("Age: %d\n", john.age);
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### 5.2 Typedef - Creating Type Aliases
|
|
|
|
`typedef` creates shorter names:
|
|
|
|
```c
|
|
// Without typedef
|
|
struct Person {
|
|
char name[50];
|
|
int age;
|
|
};
|
|
struct Person john; // Must write "struct"
|
|
|
|
// With typedef
|
|
typedef struct {
|
|
char name[50];
|
|
int age;
|
|
} Person;
|
|
|
|
Person john; // Cleaner!
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
typedef struct {
|
|
float x, y;
|
|
} Clay_Vector2;
|
|
|
|
typedef struct {
|
|
float width, height;
|
|
} Clay_Dimensions;
|
|
|
|
typedef struct {
|
|
float r, g, b, a;
|
|
} Clay_Color;
|
|
```
|
|
|
|
### 5.3 Nested Structs
|
|
|
|
Structs can contain other structs:
|
|
|
|
```c
|
|
typedef struct {
|
|
float x, y;
|
|
} Point;
|
|
|
|
typedef struct {
|
|
Point topLeft;
|
|
Point bottomRight;
|
|
} Rectangle;
|
|
|
|
int main(void) {
|
|
Rectangle rect;
|
|
rect.topLeft.x = 0.0f;
|
|
rect.topLeft.y = 0.0f;
|
|
rect.bottomRight.x = 100.0f;
|
|
rect.bottomRight.y = 50.0f;
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
typedef struct {
|
|
Clay_Vector2 x, y; // Nested struct
|
|
} Clay_BoundingBox;
|
|
|
|
typedef struct {
|
|
Clay_BoundingBox boundingBox; // Nested
|
|
Clay_Dimensions dimensions; // Nested
|
|
Clay_LayoutConfig config; // Nested
|
|
} Clay_LayoutElement;
|
|
```
|
|
|
|
### 5.4 Struct Initialization
|
|
|
|
Multiple ways to initialize structs:
|
|
|
|
```c
|
|
typedef struct {
|
|
int x;
|
|
int y;
|
|
int z;
|
|
} Point3D;
|
|
|
|
// Method 1: Member by member
|
|
Point3D p1;
|
|
p1.x = 1;
|
|
p1.y = 2;
|
|
p1.z = 3;
|
|
|
|
// Method 2: Initializer list (order matters)
|
|
Point3D p2 = {1, 2, 3};
|
|
|
|
// Method 3: Designated initializers (C99+)
|
|
Point3D p3 = {.x = 1, .y = 2, .z = 3};
|
|
Point3D p4 = {.z = 3, .x = 1, .y = 2}; // Order doesn't matter!
|
|
|
|
// Partial initialization (rest = 0)
|
|
Point3D p5 = {.x = 1}; // y=0, z=0
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
// Designated initializers for clean API
|
|
Clay_Color white = {.r = 255, .g = 255, .b = 255, .a = 255};
|
|
|
|
Clay_Dimensions size = {.width = 100, .height = 50};
|
|
|
|
Clay_BoundingBox box = {
|
|
.x = {.min = 0, .max = 100},
|
|
.y = {.min = 0, .max = 50}
|
|
};
|
|
```
|
|
|
|
### 5.5 Pointers to Structs
|
|
|
|
Use `->` to access members through pointers:
|
|
|
|
```c
|
|
typedef struct {
|
|
int x, y;
|
|
} Point;
|
|
|
|
int main(void) {
|
|
Point p = {10, 20};
|
|
Point *ptr = &p;
|
|
|
|
// Two ways to access through pointer:
|
|
(*ptr).x = 30; // Dereference then access
|
|
ptr->y = 40; // Arrow operator (cleaner)
|
|
|
|
printf("(%d, %d)\n", p.x, p.y); // (30, 40)
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
Clay_LayoutElement *element = GetLayoutElement();
|
|
|
|
// Access members with ->
|
|
element->dimensions.width = 100;
|
|
element->dimensions.height = 50;
|
|
element->childrenOrTextContent.children.length = 0;
|
|
```
|
|
|
|
### 5.6 Forward Declarations
|
|
|
|
Declare struct name before definition:
|
|
|
|
```c
|
|
// Forward declaration
|
|
typedef struct Clay_Context Clay_Context;
|
|
|
|
// Can now use Clay_Context* in other structs
|
|
typedef struct {
|
|
Clay_Context *context;
|
|
} SomeStruct;
|
|
|
|
// Full definition later
|
|
struct Clay_Context {
|
|
int32_t maxElementCount;
|
|
// ... other members
|
|
};
|
|
```
|
|
|
|
**Clay Example** (from clay.h:287):
|
|
```c
|
|
typedef struct Clay_Context Clay_Context;
|
|
|
|
// Used before full definition
|
|
Clay_Context* Clay_GetCurrentContext(void);
|
|
```
|
|
|
|
### 5.7 Key Concepts Learned
|
|
- ✅ struct for grouping data
|
|
- ✅ typedef for type aliases
|
|
- ✅ Nested structs
|
|
- ✅ Designated initializers
|
|
- ✅ -> operator for pointer member access
|
|
- ✅ Forward declarations
|
|
|
|
---
|
|
|
|
## Chapter 6: Arrays and Memory {#chapter-6}
|
|
|
|
### 6.1 Static Arrays
|
|
|
|
Fixed-size arrays declared at compile time:
|
|
|
|
```c
|
|
int main(void) {
|
|
int numbers[5]; // Array of 5 ints
|
|
numbers[0] = 10; // First element
|
|
numbers[4] = 50; // Last element
|
|
|
|
// Initialize on declaration
|
|
int values[5] = {1, 2, 3, 4, 5};
|
|
|
|
// Partial initialization (rest = 0)
|
|
int zeros[10] = {0}; // All zeros
|
|
|
|
// Size inferred from initializer
|
|
int items[] = {10, 20, 30}; // Size = 3
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### 6.2 Array and Pointer Relationship
|
|
|
|
**Important**: Array names decay to pointers!
|
|
|
|
```c
|
|
int main(void) {
|
|
int arr[5] = {10, 20, 30, 40, 50};
|
|
|
|
int *p = arr; // arr decays to pointer to first element
|
|
|
|
printf("%d\n", arr[0]); // 10
|
|
printf("%d\n", *arr); // 10 (same)
|
|
printf("%d\n", *(arr+1)); // 20
|
|
printf("%d\n", arr[1]); // 20 (same)
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### 6.3 Passing Arrays to Functions
|
|
|
|
Arrays are always passed as pointers:
|
|
|
|
```c
|
|
// These are equivalent:
|
|
void processArray1(int arr[], int size) { }
|
|
void processArray2(int *arr, int size) { }
|
|
|
|
int main(void) {
|
|
int numbers[5] = {1, 2, 3, 4, 5};
|
|
|
|
// Must pass size separately!
|
|
processArray1(numbers, 5);
|
|
processArray2(numbers, 5);
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
// Takes pointer and length
|
|
static inline void Clay__MeasureTextCached(
|
|
Clay_String *text, // Pointer to string (char array)
|
|
Clay_TextElementConfig *config
|
|
) {
|
|
// text->chars is char array
|
|
// text->length is size
|
|
}
|
|
```
|
|
|
|
### 6.4 Multidimensional Arrays
|
|
|
|
```c
|
|
int main(void) {
|
|
// 2D array: 3 rows, 4 columns
|
|
int matrix[3][4] = {
|
|
{1, 2, 3, 4},
|
|
{5, 6, 7, 8},
|
|
{9, 10, 11, 12}
|
|
};
|
|
|
|
printf("%d\n", matrix[0][0]); // 1
|
|
printf("%d\n", matrix[1][2]); // 7
|
|
printf("%d\n", matrix[2][3]); // 12
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### 6.5 Flexible Array Members
|
|
|
|
Last member of struct can be flexible:
|
|
|
|
```c
|
|
typedef struct {
|
|
int length;
|
|
int items[]; // Flexible array (must be last)
|
|
} DynamicArray;
|
|
|
|
// Allocate with specific size
|
|
DynamicArray *arr = malloc(sizeof(DynamicArray) + 10 * sizeof(int));
|
|
arr->length = 10;
|
|
arr->items[0] = 100;
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
typedef struct {
|
|
int32_t capacity;
|
|
int32_t length;
|
|
Clay_ElementId *internalArray; // Pointer acts like flexible array
|
|
} Clay__ElementIdArray;
|
|
```
|
|
|
|
### 6.6 String Arrays
|
|
|
|
Strings in C are char arrays ending with '\0':
|
|
|
|
```c
|
|
int main(void) {
|
|
// String literal
|
|
char *str1 = "Hello"; // Points to read-only memory
|
|
|
|
// Char array
|
|
char str2[] = "Hello"; // Mutable, size = 6 (includes \0)
|
|
|
|
// Explicit size
|
|
char str3[10] = "Hello"; // Rest filled with \0
|
|
|
|
// Character by character
|
|
char str4[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
// Clay uses explicit length instead of \0
|
|
typedef struct {
|
|
int32_t length;
|
|
const char *chars; // Not null-terminated!
|
|
} Clay_String;
|
|
|
|
// Macro to create from string literal
|
|
#define CLAY_STRING(stringContents) \
|
|
(Clay_String) { .length = sizeof(stringContents) - 1, .chars = stringContents }
|
|
```
|
|
|
|
### 6.7 sizeof with Arrays
|
|
|
|
```c
|
|
int main(void) {
|
|
int arr[10];
|
|
|
|
size_t arrayBytes = sizeof(arr); // 40 (10 * 4)
|
|
size_t elementBytes = sizeof(arr[0]); // 4
|
|
size_t arrayLength = sizeof(arr) / sizeof(arr[0]); // 10
|
|
|
|
// Warning: This doesn't work with pointers!
|
|
int *p = arr;
|
|
size_t pointerSize = sizeof(p); // 8 (on 64-bit), not 40!
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### 6.8 Key Concepts Learned
|
|
- ✅ Static array declaration
|
|
- ✅ Array initialization
|
|
- ✅ Arrays decay to pointers
|
|
- ✅ Passing arrays to functions
|
|
- ✅ Multidimensional arrays
|
|
- ✅ C strings (null-terminated char arrays)
|
|
- ✅ sizeof with arrays
|
|
|
|
---
|
|
|
|
## Chapter 7: Preprocessor and Macros {#chapter-7}
|
|
|
|
### 7.1 What is the Preprocessor?
|
|
|
|
The preprocessor runs BEFORE compilation and performs text substitution:
|
|
|
|
```c
|
|
#include <stdio.h> // Insert file contents
|
|
#define MAX 100 // Text replacement
|
|
|
|
int main(void) {
|
|
int arr[MAX]; // Becomes: int arr[100];
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### 7.2 #define - Simple Macros
|
|
|
|
```c
|
|
#define PI 3.14159
|
|
#define MAX_SIZE 1000
|
|
#define PROGRAM_NAME "MyApp"
|
|
|
|
int main(void) {
|
|
float radius = 5.0f;
|
|
float area = PI * radius * radius; // PI replaced with 3.14159
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h:102):
|
|
```c
|
|
#define CLAY_VERSION_MAJOR 0
|
|
#define CLAY_VERSION_MINOR 12
|
|
#define CLAY_VERSION_PATCH 0
|
|
```
|
|
|
|
### 7.3 #ifdef and Conditional Compilation
|
|
|
|
```c
|
|
#define DEBUG
|
|
|
|
int main(void) {
|
|
#ifdef DEBUG
|
|
printf("Debug mode enabled\n");
|
|
#endif
|
|
|
|
#ifndef RELEASE
|
|
printf("Not release mode\n");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h:82):
|
|
```c
|
|
#ifndef CLAY_HEADER
|
|
#define CLAY_HEADER
|
|
|
|
// Header contents here...
|
|
|
|
#endif // CLAY_HEADER
|
|
```
|
|
|
|
This prevents multiple inclusion of the same header.
|
|
|
|
### 7.4 Function-like Macros
|
|
|
|
Macros can take arguments:
|
|
|
|
```c
|
|
#define SQUARE(x) ((x) * (x))
|
|
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
|
|
|
int main(void) {
|
|
int result = SQUARE(5); // (5) * (5) = 25
|
|
int max = MAX(10, 20); // 20
|
|
|
|
// Warning: Be careful!
|
|
int bad = SQUARE(2 + 3); // (2 + 3) * (2 + 3) = 25 ✓
|
|
// Without parentheses would be: 2 + 3 * 2 + 3 = 11 ✗
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h:111):
|
|
```c
|
|
#define CLAY__MAX(x, y) (((x) > (y)) ? (x) : (y))
|
|
#define CLAY__MIN(x, y) (((x) < (y)) ? (x) : (y))
|
|
```
|
|
|
|
### 7.5 Multi-line Macros
|
|
|
|
Use backslash to continue lines:
|
|
|
|
```c
|
|
#define SWAP(a, b) \
|
|
do { \
|
|
typeof(a) temp = a; \
|
|
a = b; \
|
|
b = temp; \
|
|
} while(0)
|
|
|
|
int main(void) {
|
|
int x = 5, y = 10;
|
|
SWAP(x, y);
|
|
printf("%d %d\n", x, y); // 10 5
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Why `do { } while(0)`?** It ensures the macro behaves like a statement:
|
|
|
|
```c
|
|
if (condition)
|
|
SWAP(x, y); // Works correctly
|
|
else
|
|
other();
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
#define CLAY__ASSERT(condition) \
|
|
if (!(condition)) { \
|
|
Clay__currentContext->errorHandler.errorHandlerFunction( \
|
|
CLAY__INIT(Clay_ErrorData) { \
|
|
.errorType = CLAY_ERROR_TYPE_ASSERTION_FAILED, \
|
|
.errorText = CLAY_STRING("Assertion failed") \
|
|
} \
|
|
); \
|
|
}
|
|
```
|
|
|
|
### 7.6 Stringification (#)
|
|
|
|
Convert macro argument to string:
|
|
|
|
```c
|
|
#define TO_STRING(x) #x
|
|
|
|
int main(void) {
|
|
printf("%s\n", TO_STRING(Hello)); // "Hello"
|
|
printf("%s\n", TO_STRING(123)); // "123"
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
#define CLAY__STRINGIFY(x) #x
|
|
#define CLAY__VERSION_STRING \
|
|
CLAY__STRINGIFY(CLAY_VERSION_MAJOR) "." \
|
|
CLAY__STRINGIFY(CLAY_VERSION_MINOR) "." \
|
|
CLAY__STRINGIFY(CLAY_VERSION_PATCH)
|
|
// Results in "0.12.0"
|
|
```
|
|
|
|
### 7.7 Token Pasting (##)
|
|
|
|
Concatenate tokens:
|
|
|
|
```c
|
|
#define CONCAT(a, b) a##b
|
|
|
|
int main(void) {
|
|
int xy = 100;
|
|
int value = CONCAT(x, y); // Becomes: xy
|
|
printf("%d\n", value); // 100
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
#define CLAY__ARRAY_DEFINE(typeName, arrayName) \
|
|
typedef struct { \
|
|
int32_t capacity; \
|
|
int32_t length; \
|
|
typeName *internalArray; \
|
|
} arrayName;
|
|
|
|
// Creates type: Clay__int32_tArray
|
|
CLAY__ARRAY_DEFINE(int32_t, Clay__int32_tArray)
|
|
```
|
|
|
|
### 7.8 Predefined Macros
|
|
|
|
C provides built-in macros:
|
|
|
|
```c
|
|
#include <stdio.h>
|
|
|
|
int main(void) {
|
|
printf("File: %s\n", __FILE__); // Current file name
|
|
printf("Line: %d\n", __LINE__); // Current line number
|
|
printf("Date: %s\n", __DATE__); // Compilation date
|
|
printf("Time: %s\n", __TIME__); // Compilation time
|
|
printf("Function: %s\n", __func__); // Current function name (C99)
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### 7.9 Header Guards
|
|
|
|
Prevent multiple inclusion:
|
|
|
|
```c
|
|
// myheader.h
|
|
#ifndef MYHEADER_H
|
|
#define MYHEADER_H
|
|
|
|
// Header contents here
|
|
|
|
#endif // MYHEADER_H
|
|
```
|
|
|
|
**Clay Example** (from clay.h:82):
|
|
```c
|
|
#ifndef CLAY_HEADER
|
|
#define CLAY_HEADER
|
|
|
|
// All Clay declarations
|
|
|
|
#endif // CLAY_HEADER
|
|
```
|
|
|
|
### 7.10 Key Concepts Learned
|
|
- ✅ Preprocessor runs before compilation
|
|
- ✅ #define for constants and macros
|
|
- ✅ #ifdef, #ifndef for conditional compilation
|
|
- ✅ Function-like macros with parameters
|
|
- ✅ Multi-line macros with backslash
|
|
- ✅ # for stringification
|
|
- ✅ ## for token pasting
|
|
- ✅ Header guards
|
|
|
|
---
|
|
|
|
## Chapter 8: Advanced Macros and Metaprogramming {#chapter-8}
|
|
|
|
### 8.1 Variadic Macros
|
|
|
|
Macros that accept variable number of arguments:
|
|
|
|
```c
|
|
#define LOG(format, ...) \
|
|
printf("[LOG] " format "\n", __VA_ARGS__)
|
|
|
|
int main(void) {
|
|
LOG("Value: %d", 42);
|
|
LOG("X: %d, Y: %d", 10, 20);
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**`__VA_ARGS__`** represents all extra arguments.
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
#define CLAY__INIT(type) \
|
|
(type)
|
|
|
|
// Usage with designated initializers
|
|
Clay_Color color = CLAY__INIT(Clay_Color) {
|
|
.r = 255, .g = 0, .b = 0, .a = 255
|
|
};
|
|
```
|
|
|
|
### 8.2 X-Macros Pattern
|
|
|
|
Generate code from data:
|
|
|
|
```c
|
|
// Define data once
|
|
#define COLOR_LIST \
|
|
X(RED, 0xFF0000) \
|
|
X(GREEN, 0x00FF00) \
|
|
X(BLUE, 0x0000FF)
|
|
|
|
// Generate enum
|
|
enum Colors {
|
|
#define X(name, value) COLOR_##name,
|
|
COLOR_LIST
|
|
#undef X
|
|
};
|
|
|
|
// Generate array
|
|
const char* colorNames[] = {
|
|
#define X(name, value) #name,
|
|
COLOR_LIST
|
|
#undef X
|
|
};
|
|
|
|
int main(void) {
|
|
printf("%s\n", colorNames[COLOR_RED]); // "RED"
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### 8.3 Generic Macros with _Generic (C11)
|
|
|
|
Type-based selection at compile time:
|
|
|
|
```c
|
|
#define max(a, b) _Generic((a), \
|
|
int: max_int, \
|
|
float: max_float, \
|
|
double: max_double \
|
|
)(a, b)
|
|
|
|
int max_int(int a, int b) { return a > b ? a : b; }
|
|
float max_float(float a, float b) { return a > b ? a : b; }
|
|
double max_double(double a, double b) { return a > b ? a : b; }
|
|
|
|
int main(void) {
|
|
int i = max(5, 10); // Calls max_int
|
|
float f = max(5.5f, 10.2f); // Calls max_float
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### 8.4 Compound Literals
|
|
|
|
Create temporary struct values:
|
|
|
|
```c
|
|
typedef struct {
|
|
int x, y;
|
|
} Point;
|
|
|
|
void printPoint(Point p) {
|
|
printf("(%d, %d)\n", p.x, p.y);
|
|
}
|
|
|
|
int main(void) {
|
|
// Compound literal
|
|
printPoint((Point){.x = 10, .y = 20});
|
|
|
|
// Another example
|
|
Point *p = &(Point){.x = 5, .y = 15};
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
// Macro uses compound literals for clean API
|
|
#define CLAY_COLOR(r, g, b, a) \
|
|
(Clay_Color) {.r = r, .g = b, .b = b, .a = a}
|
|
|
|
// Usage
|
|
Clay_Color red = CLAY_COLOR(255, 0, 0, 255);
|
|
```
|
|
|
|
### 8.5 Statement Expressions (GCC Extension)
|
|
|
|
Create macros that return values:
|
|
|
|
```c
|
|
#define MAX(a, b) ({ \
|
|
typeof(a) _a = (a); \
|
|
typeof(b) _b = (b); \
|
|
_a > _b ? _a : _b; \
|
|
})
|
|
|
|
int main(void) {
|
|
int x = MAX(5 + 2, 3 + 4); // Evaluates each expression once
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### 8.6 The For-Loop Macro Trick
|
|
|
|
Create scope-based macros:
|
|
|
|
```c
|
|
#define WITH_LOCK(mutex) \
|
|
for (int _i = (lock(mutex), 0); _i < 1; _i++, unlock(mutex))
|
|
|
|
// Usage
|
|
WITH_LOCK(&myMutex) {
|
|
// Critical section
|
|
// mutex automatically unlocked when block exits
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h:2016) - This is ADVANCED:
|
|
```c
|
|
#define CLAY(...) \
|
|
for ( \
|
|
CLAY__ELEMENT_DEFINITION_LATCH = ( \
|
|
Clay__OpenElement(), \
|
|
Clay__ConfigureOpenElement(__VA_ARGS__), \
|
|
0 \
|
|
); \
|
|
CLAY__ELEMENT_DEFINITION_LATCH < 1; \
|
|
CLAY__ELEMENT_DEFINITION_LATCH = 1, Clay__CloseElement() \
|
|
)
|
|
|
|
// Usage creates automatic open/close pairs
|
|
CLAY({ .layout = { .padding = CLAY_PADDING_ALL(8) } }) {
|
|
// Child elements
|
|
}
|
|
// Automatically closes element
|
|
```
|
|
|
|
**How it works:**
|
|
1. **Init**: `Clay__OpenElement()`, configure, set latch to 0
|
|
2. **Condition**: `latch < 1` is true (0 < 1), enter loop
|
|
3. **Body**: User code executes
|
|
4. **Increment**: Set latch to 1, `Clay__CloseElement()`
|
|
5. **Condition**: `latch < 1` is false (1 < 1), exit loop
|
|
|
|
This ensures `CloseElement` is always called!
|
|
|
|
### 8.7 Designated Initializers in Macros
|
|
|
|
```c
|
|
#define CREATE_RECT(w, h) \
|
|
(Rectangle) { \
|
|
.width = (w), \
|
|
.height = (h), \
|
|
.x = 0, \
|
|
.y = 0 \
|
|
}
|
|
|
|
int main(void) {
|
|
Rectangle r = CREATE_RECT(100, 50);
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
#define CLAY_LAYOUT(...) \
|
|
(Clay_LayoutConfig) { __VA_ARGS__ }
|
|
|
|
#define CLAY_SIZING_FIT(min, max) \
|
|
(Clay_Sizing) { \
|
|
.type = CLAY_SIZING_TYPE_FIT, \
|
|
.size = { .minMax = { .min = (min), .max = (max) } } \
|
|
}
|
|
|
|
// Usage
|
|
Clay_LayoutConfig layout = CLAY_LAYOUT(
|
|
.sizing = { .width = CLAY_SIZING_FIT(100, 500) }
|
|
);
|
|
```
|
|
|
|
### 8.8 Macro Debugging Tips
|
|
|
|
```c
|
|
// Use #pragma to see macro expansion
|
|
#define COMPLEX_MACRO(x) ((x) * 2 + 1)
|
|
|
|
int main(void) {
|
|
#pragma message "COMPLEX_MACRO(5) expands to:"
|
|
int result = COMPLEX_MACRO(5);
|
|
return 0;
|
|
}
|
|
|
|
// Compile with gcc -E to see preprocessor output
|
|
```
|
|
|
|
### 8.9 Key Concepts Learned
|
|
- ✅ Variadic macros (__VA_ARGS__)
|
|
- ✅ X-Macros for code generation
|
|
- ✅ _Generic for type-based selection
|
|
- ✅ Compound literals
|
|
- ✅ Statement expressions
|
|
- ✅ For-loop macro trick
|
|
- ✅ Designated initializers in macros
|
|
|
|
---
|
|
|
|
## Chapter 9: Memory Management {#chapter-9}
|
|
|
|
### 9.1 Stack vs Heap
|
|
|
|
**Stack Memory:**
|
|
- Automatic allocation/deallocation
|
|
- Fast
|
|
- Limited size
|
|
- Variables disappear when function returns
|
|
|
|
```c
|
|
void function(void) {
|
|
int x = 10; // On stack
|
|
char str[100]; // On stack
|
|
} // x and str are destroyed here
|
|
```
|
|
|
|
**Heap Memory:**
|
|
- Manual allocation (malloc) and deallocation (free)
|
|
- Slower
|
|
- Large size available
|
|
- Persists until freed
|
|
|
|
```c
|
|
#include <stdlib.h>
|
|
|
|
void function(void) {
|
|
int *p = malloc(sizeof(int)); // On heap
|
|
*p = 10;
|
|
free(p); // Must manually free!
|
|
}
|
|
```
|
|
|
|
### 9.2 malloc, calloc, realloc, free
|
|
|
|
```c
|
|
#include <stdlib.h>
|
|
|
|
int main(void) {
|
|
// malloc - allocate uninitialized memory
|
|
int *arr1 = malloc(10 * sizeof(int));
|
|
if (arr1 == NULL) {
|
|
// Allocation failed!
|
|
return 1;
|
|
}
|
|
|
|
// calloc - allocate zero-initialized memory
|
|
int *arr2 = calloc(10, sizeof(int)); // All zeros
|
|
|
|
// realloc - resize allocated memory
|
|
arr1 = realloc(arr1, 20 * sizeof(int));
|
|
|
|
// free - deallocate memory
|
|
free(arr1);
|
|
free(arr2);
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### 9.3 Common Memory Errors
|
|
|
|
```c
|
|
// 1. Memory leak - allocated but never freed
|
|
void leak(void) {
|
|
int *p = malloc(sizeof(int));
|
|
// Forgot to free(p)!
|
|
}
|
|
|
|
// 2. Use after free
|
|
void useAfterFree(void) {
|
|
int *p = malloc(sizeof(int));
|
|
free(p);
|
|
*p = 10; // DANGEROUS! Undefined behavior
|
|
}
|
|
|
|
// 3. Double free
|
|
void doubleFree(void) {
|
|
int *p = malloc(sizeof(int));
|
|
free(p);
|
|
free(p); // CRASH!
|
|
}
|
|
|
|
// 4. Accessing uninitialized memory
|
|
void uninit(void) {
|
|
int *p = malloc(sizeof(int));
|
|
printf("%d\n", *p); // Random value!
|
|
free(p);
|
|
}
|
|
```
|
|
|
|
### 9.4 Arena Allocators
|
|
|
|
Instead of many malloc/free calls, allocate one large block:
|
|
|
|
```c
|
|
typedef struct {
|
|
char *memory;
|
|
size_t size;
|
|
size_t used;
|
|
} Arena;
|
|
|
|
void Arena_Init(Arena *arena, size_t size) {
|
|
arena->memory = malloc(size);
|
|
arena->size = size;
|
|
arena->used = 0;
|
|
}
|
|
|
|
void* Arena_Alloc(Arena *arena, size_t size) {
|
|
if (arena->used + size > arena->size) {
|
|
return NULL; // Out of memory
|
|
}
|
|
void *ptr = arena->memory + arena->used;
|
|
arena->used += size;
|
|
return ptr;
|
|
}
|
|
|
|
void Arena_Free(Arena *arena) {
|
|
free(arena->memory); // Free everything at once
|
|
}
|
|
|
|
int main(void) {
|
|
Arena arena;
|
|
Arena_Init(&arena, 1024 * 1024); // 1MB
|
|
|
|
int *arr1 = Arena_Alloc(&arena, 100 * sizeof(int));
|
|
float *arr2 = Arena_Alloc(&arena, 50 * sizeof(float));
|
|
|
|
// Use allocations...
|
|
|
|
Arena_Free(&arena); // Free everything
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h:185):
|
|
```c
|
|
typedef struct {
|
|
uintptr_t nextAllocation;
|
|
size_t capacity;
|
|
char *memory;
|
|
} Clay_Arena;
|
|
|
|
// Clay allocates everything from arenas - no malloc in hot path!
|
|
void* Clay__Array_Allocate_Arena(
|
|
int32_t capacity,
|
|
uint32_t itemSize,
|
|
Clay_Arena *arena
|
|
) {
|
|
size_t totalSizeBytes = capacity * itemSize;
|
|
uintptr_t nextAllocation = arena->nextAllocation + totalSizeBytes;
|
|
|
|
if (nextAllocation <= arena->capacity) {
|
|
void *allocation = (void*)(arena->memory + arena->nextAllocation);
|
|
arena->nextAllocation = nextAllocation;
|
|
return allocation;
|
|
}
|
|
|
|
return NULL; // Out of memory
|
|
}
|
|
```
|
|
|
|
### 9.5 Memory Alignment
|
|
|
|
CPUs prefer aligned memory access:
|
|
|
|
```c
|
|
#include <stdint.h>
|
|
|
|
// Proper alignment
|
|
struct Aligned {
|
|
uint64_t a; // 8 bytes, aligned to 8
|
|
uint32_t b; // 4 bytes
|
|
uint32_t c; // 4 bytes
|
|
}; // Total: 16 bytes
|
|
|
|
// Poor alignment - compiler adds padding
|
|
struct Unaligned {
|
|
uint8_t a; // 1 byte
|
|
// 7 bytes padding
|
|
uint64_t b; // 8 bytes
|
|
uint8_t c; // 1 byte
|
|
// 7 bytes padding
|
|
}; // Total: 24 bytes instead of 10!
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
// Careful struct layout for performance
|
|
typedef struct {
|
|
float width, height; // 8 bytes total
|
|
} Clay_Dimensions; // Aligned to 4 bytes
|
|
|
|
typedef struct {
|
|
Clay_Vector2 x, y; // 16 bytes total
|
|
} Clay_BoundingBox; // Aligned to 4 bytes
|
|
```
|
|
|
|
### 9.6 Memory Pools
|
|
|
|
Pre-allocate objects of same size:
|
|
|
|
```c
|
|
#define POOL_SIZE 100
|
|
|
|
typedef struct Node {
|
|
int value;
|
|
struct Node *next;
|
|
} Node;
|
|
|
|
typedef struct {
|
|
Node nodes[POOL_SIZE];
|
|
Node *freeList;
|
|
} NodePool;
|
|
|
|
void Pool_Init(NodePool *pool) {
|
|
// Chain all nodes into free list
|
|
for (int i = 0; i < POOL_SIZE - 1; i++) {
|
|
pool->nodes[i].next = &pool->nodes[i + 1];
|
|
}
|
|
pool->nodes[POOL_SIZE - 1].next = NULL;
|
|
pool->freeList = &pool->nodes[0];
|
|
}
|
|
|
|
Node* Pool_Alloc(NodePool *pool) {
|
|
if (pool->freeList == NULL) return NULL;
|
|
|
|
Node *node = pool->freeList;
|
|
pool->freeList = node->next;
|
|
return node;
|
|
}
|
|
|
|
void Pool_Free(NodePool *pool, Node *node) {
|
|
node->next = pool->freeList;
|
|
pool->freeList = node;
|
|
}
|
|
```
|
|
|
|
### 9.7 Key Concepts Learned
|
|
- ✅ Stack vs heap memory
|
|
- ✅ malloc, calloc, realloc, free
|
|
- ✅ Common memory errors
|
|
- ✅ Arena allocators
|
|
- ✅ Memory alignment
|
|
- ✅ Memory pools
|
|
|
|
---
|
|
|
|
## Chapter 10: Header Files and Project Organization {#chapter-10}
|
|
|
|
### 10.1 Header Files Basics
|
|
|
|
**Header file (.h)**: Declarations (interface)
|
|
**Source file (.c)**: Definitions (implementation)
|
|
|
|
```c
|
|
// math_utils.h
|
|
#ifndef MATH_UTILS_H
|
|
#define MATH_UTILS_H
|
|
|
|
int add(int a, int b); // Declaration only
|
|
int multiply(int a, int b);
|
|
|
|
#endif
|
|
```
|
|
|
|
```c
|
|
// math_utils.c
|
|
#include "math_utils.h"
|
|
|
|
int add(int a, int b) { // Definition
|
|
return a + b;
|
|
}
|
|
|
|
int multiply(int a, int b) {
|
|
return a * b;
|
|
}
|
|
```
|
|
|
|
```c
|
|
// main.c
|
|
#include "math_utils.h"
|
|
|
|
int main(void) {
|
|
int result = add(5, 3);
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### 10.2 Header Guards
|
|
|
|
Prevent multiple inclusion:
|
|
|
|
```c
|
|
// myheader.h
|
|
#ifndef MYHEADER_H
|
|
#define MYHEADER_H
|
|
|
|
// Declarations
|
|
|
|
#endif // MYHEADER_H
|
|
```
|
|
|
|
Alternative (non-standard but widely supported):
|
|
```c
|
|
#pragma once
|
|
|
|
// Declarations
|
|
```
|
|
|
|
### 10.3 Include Order
|
|
|
|
Best practice:
|
|
|
|
```c
|
|
// In myfile.c:
|
|
|
|
// 1. Corresponding header
|
|
#include "myfile.h"
|
|
|
|
// 2. System headers
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
// 3. Third-party headers
|
|
#include "external_lib.h"
|
|
|
|
// 4. Project headers
|
|
#include "project_utils.h"
|
|
```
|
|
|
|
### 10.4 Single-Header Library Pattern
|
|
|
|
Entire library in one header file:
|
|
|
|
```c
|
|
// mylib.h
|
|
#ifndef MYLIB_H
|
|
#define MYLIB_H
|
|
|
|
// Declarations (always included)
|
|
void myFunction(void);
|
|
|
|
// Implementation (included only once)
|
|
#ifdef MYLIB_IMPLEMENTATION
|
|
|
|
void myFunction(void) {
|
|
// Implementation here
|
|
}
|
|
|
|
#endif // MYLIB_IMPLEMENTATION
|
|
#endif // MYLIB_H
|
|
```
|
|
|
|
**Usage:**
|
|
```c
|
|
// In ONE .c file:
|
|
#define MYLIB_IMPLEMENTATION
|
|
#include "mylib.h"
|
|
|
|
// In other files:
|
|
#include "mylib.h"
|
|
```
|
|
|
|
**Clay Example** (clay.h structure):
|
|
```c
|
|
#ifndef CLAY_HEADER
|
|
#define CLAY_HEADER
|
|
|
|
// ===== PUBLIC API DECLARATIONS =====
|
|
typedef struct { /* ... */ } Clay_Dimensions;
|
|
void Clay_BeginLayout(void);
|
|
// ... more declarations
|
|
|
|
// ===== IMPLEMENTATION =====
|
|
#ifdef CLAY_IMPLEMENTATION
|
|
|
|
// All implementation code here
|
|
void Clay_BeginLayout(void) {
|
|
// ...
|
|
}
|
|
|
|
#endif // CLAY_IMPLEMENTATION
|
|
#endif // CLAY_HEADER
|
|
```
|
|
|
|
### 10.5 Forward Declarations
|
|
|
|
Declare types before full definition:
|
|
|
|
```c
|
|
// Forward declaration
|
|
typedef struct Node Node;
|
|
|
|
typedef struct {
|
|
Node *next; // Can use pointer to Node
|
|
} List;
|
|
|
|
// Full definition later
|
|
struct Node {
|
|
int value;
|
|
Node *next;
|
|
};
|
|
```
|
|
|
|
**Clay Example** (from clay.h:287):
|
|
```c
|
|
typedef struct Clay_Context Clay_Context;
|
|
|
|
// Can now use Clay_Context* in functions
|
|
Clay_Context* Clay_GetCurrentContext(void);
|
|
|
|
// Full definition comes later (line 1900+)
|
|
struct Clay_Context {
|
|
// ...
|
|
};
|
|
```
|
|
|
|
### 10.6 Opaque Pointers
|
|
|
|
Hide implementation details:
|
|
|
|
```c
|
|
// widget.h
|
|
typedef struct Widget Widget; // Opaque type
|
|
|
|
Widget* Widget_Create(void);
|
|
void Widget_Destroy(Widget *w);
|
|
void Widget_SetValue(Widget *w, int value);
|
|
```
|
|
|
|
```c
|
|
// widget.c
|
|
struct Widget { // Full definition only in .c file
|
|
int value;
|
|
int internal_state;
|
|
};
|
|
|
|
Widget* Widget_Create(void) {
|
|
Widget *w = malloc(sizeof(Widget));
|
|
w->value = 0;
|
|
w->internal_state = 0;
|
|
return w;
|
|
}
|
|
```
|
|
|
|
Users can't access internal members directly!
|
|
|
|
### 10.7 Conditional Compilation for Platforms
|
|
|
|
```c
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#define SLEEP(ms) Sleep(ms)
|
|
#else
|
|
#include <unistd.h>
|
|
#define SLEEP(ms) usleep((ms) * 1000)
|
|
#endif
|
|
|
|
int main(void) {
|
|
SLEEP(1000); // Sleep 1 second on any platform
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h:88):
|
|
```c
|
|
#if defined(_MSC_VER)
|
|
#define CLAY_WASM __declspec(dllexport)
|
|
#elif defined(__GNUC__) || defined(__clang__)
|
|
#define CLAY_WASM __attribute__((visibility("default")))
|
|
#else
|
|
#define CLAY_WASM
|
|
#endif
|
|
```
|
|
|
|
### 10.8 Key Concepts Learned
|
|
- ✅ Header vs source files
|
|
- ✅ Header guards
|
|
- ✅ Include order best practices
|
|
- ✅ Single-header library pattern
|
|
- ✅ Forward declarations
|
|
- ✅ Opaque pointers
|
|
- ✅ Platform-specific code
|
|
|
|
---
|
|
|
|
## Chapter 11: Enums and Unions {#chapter-11}
|
|
|
|
### 11.1 Enums - Named Constants
|
|
|
|
```c
|
|
enum Color {
|
|
COLOR_RED, // 0
|
|
COLOR_GREEN, // 1
|
|
COLOR_BLUE // 2
|
|
};
|
|
|
|
int main(void) {
|
|
enum Color myColor = COLOR_RED;
|
|
|
|
if (myColor == COLOR_RED) {
|
|
printf("Red!\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### 11.2 Enums with Typedef
|
|
|
|
```c
|
|
typedef enum {
|
|
COLOR_RED,
|
|
COLOR_GREEN,
|
|
COLOR_BLUE
|
|
} Color;
|
|
|
|
Color myColor = COLOR_RED; // Cleaner syntax
|
|
```
|
|
|
|
### 11.3 Custom Enum Values
|
|
|
|
```c
|
|
typedef enum {
|
|
ERROR_NONE = 0,
|
|
ERROR_FILE_NOT_FOUND = 1,
|
|
ERROR_PERMISSION_DENIED = 2,
|
|
ERROR_OUT_OF_MEMORY = 100,
|
|
ERROR_UNKNOWN = -1
|
|
} ErrorCode;
|
|
```
|
|
|
|
**Clay Example** (from clay.h:208):
|
|
```c
|
|
typedef enum {
|
|
CLAY_SIZING_TYPE_FIT,
|
|
CLAY_SIZING_TYPE_GROW,
|
|
CLAY_SIZING_TYPE_PERCENT,
|
|
CLAY_SIZING_TYPE_FIXED
|
|
} Clay_SizingType;
|
|
|
|
typedef enum {
|
|
CLAY_LAYOUT_DIRECTION_LEFT_TO_RIGHT,
|
|
CLAY_LAYOUT_DIRECTION_TOP_TO_BOTTOM
|
|
} Clay_LayoutDirection;
|
|
```
|
|
|
|
### 11.4 Enum Flags (Bit Flags)
|
|
|
|
```c
|
|
typedef enum {
|
|
FLAG_NONE = 0, // 0000
|
|
FLAG_READ = 1 << 0, // 0001
|
|
FLAG_WRITE = 1 << 1, // 0010
|
|
FLAG_EXECUTE = 1 << 2, // 0100
|
|
FLAG_ADMIN = 1 << 3 // 1000
|
|
} Permissions;
|
|
|
|
int main(void) {
|
|
Permissions perms = FLAG_READ | FLAG_WRITE; // Combine flags
|
|
|
|
if (perms & FLAG_READ) {
|
|
printf("Can read\n");
|
|
}
|
|
|
|
perms |= FLAG_EXECUTE; // Add execute
|
|
perms &= ~FLAG_WRITE; // Remove write
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h:252):
|
|
```c
|
|
typedef enum {
|
|
CLAY_CORNER_RADIUS_NONE = 0,
|
|
CLAY_CORNER_RADIUS_TOP_LEFT = 1 << 0,
|
|
CLAY_CORNER_RADIUS_TOP_RIGHT = 1 << 1,
|
|
CLAY_CORNER_RADIUS_BOTTOM_LEFT = 1 << 2,
|
|
CLAY_CORNER_RADIUS_BOTTOM_RIGHT = 1 << 3,
|
|
CLAY_CORNER_RADIUS_ALL =
|
|
CLAY_CORNER_RADIUS_TOP_LEFT |
|
|
CLAY_CORNER_RADIUS_TOP_RIGHT |
|
|
CLAY_CORNER_RADIUS_BOTTOM_LEFT |
|
|
CLAY_CORNER_RADIUS_BOTTOM_RIGHT
|
|
} Clay_CornerRadiusSet;
|
|
```
|
|
|
|
### 11.5 Unions - Same Memory, Different Types
|
|
|
|
```c
|
|
union Data {
|
|
int i;
|
|
float f;
|
|
char str[20];
|
|
};
|
|
|
|
int main(void) {
|
|
union Data data;
|
|
|
|
data.i = 10;
|
|
printf("%d\n", data.i); // 10
|
|
|
|
data.f = 3.14f; // Overwrites i
|
|
printf("%f\n", data.f); // 3.14
|
|
// data.i is now garbage!
|
|
|
|
printf("Size: %zu\n", sizeof(data)); // 20 (largest member)
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### 11.6 Tagged Unions
|
|
|
|
Track which union member is active:
|
|
|
|
```c
|
|
typedef enum {
|
|
TYPE_INT,
|
|
TYPE_FLOAT,
|
|
TYPE_STRING
|
|
} ValueType;
|
|
|
|
typedef struct {
|
|
ValueType type; // Tag
|
|
union {
|
|
int i;
|
|
float f;
|
|
char *s;
|
|
} data;
|
|
} Value;
|
|
|
|
int main(void) {
|
|
Value v;
|
|
|
|
v.type = TYPE_INT;
|
|
v.data.i = 42;
|
|
|
|
// Safe access based on tag
|
|
switch (v.type) {
|
|
case TYPE_INT:
|
|
printf("Int: %d\n", v.data.i);
|
|
break;
|
|
case TYPE_FLOAT:
|
|
printf("Float: %f\n", v.data.f);
|
|
break;
|
|
case TYPE_STRING:
|
|
printf("String: %s\n", v.data.s);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h:213):
|
|
```c
|
|
typedef struct {
|
|
Clay_SizingType type; // Tag
|
|
union {
|
|
float sizeMinMax;
|
|
struct {
|
|
float min;
|
|
float max;
|
|
} minMax;
|
|
float sizePercent;
|
|
} size;
|
|
} Clay_Sizing;
|
|
|
|
// Usage
|
|
Clay_Sizing sizing;
|
|
sizing.type = CLAY_SIZING_TYPE_FIXED;
|
|
sizing.size.sizeMinMax = 100.0f; // Safe because type is FIXED
|
|
```
|
|
|
|
### 11.7 Anonymous Unions and Structs (C11)
|
|
|
|
```c
|
|
typedef struct {
|
|
enum { TYPE_INT, TYPE_FLOAT } type;
|
|
union { // Anonymous union
|
|
int i;
|
|
float f;
|
|
}; // No name!
|
|
} Value;
|
|
|
|
int main(void) {
|
|
Value v;
|
|
v.type = TYPE_INT;
|
|
v.i = 42; // Access directly, not v.data.i
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### 11.8 Key Concepts Learned
|
|
- ✅ Enums for named constants
|
|
- ✅ Typedef with enums
|
|
- ✅ Bit flags with enums
|
|
- ✅ Unions for memory-efficient storage
|
|
- ✅ Tagged unions for type safety
|
|
- ✅ Anonymous unions
|
|
|
|
---
|
|
|
|
## Chapter 12: Function Pointers and Callbacks {#chapter-12}
|
|
|
|
### 12.1 Function Pointer Basics
|
|
|
|
Functions have addresses too!
|
|
|
|
```c
|
|
#include <stdio.h>
|
|
|
|
int add(int a, int b) {
|
|
return a + b;
|
|
}
|
|
|
|
int multiply(int a, int b) {
|
|
return a * b;
|
|
}
|
|
|
|
int main(void) {
|
|
// Function pointer syntax: return_type (*name)(params)
|
|
int (*operation)(int, int);
|
|
|
|
operation = add;
|
|
printf("%d\n", operation(5, 3)); // 8
|
|
|
|
operation = multiply;
|
|
printf("%d\n", operation(5, 3)); // 15
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### 12.2 Typedef with Function Pointers
|
|
|
|
Make syntax cleaner:
|
|
|
|
```c
|
|
// Without typedef
|
|
int (*callback)(int, int);
|
|
|
|
// With typedef
|
|
typedef int (*BinaryOperation)(int, int);
|
|
|
|
BinaryOperation callback; // Much cleaner!
|
|
```
|
|
|
|
**Clay Example** (from clay.h:166):
|
|
```c
|
|
typedef void (*Clay_ErrorHandler)(Clay_ErrorData errorData);
|
|
|
|
typedef struct {
|
|
Clay_ErrorHandler errorHandlerFunction;
|
|
void *userData;
|
|
} Clay_ErrorHandlerFunction;
|
|
```
|
|
|
|
### 12.3 Callbacks
|
|
|
|
Pass functions as parameters:
|
|
|
|
```c
|
|
#include <stdio.h>
|
|
|
|
typedef void (*Callback)(int);
|
|
|
|
void processArray(int *arr, int size, Callback func) {
|
|
for (int i = 0; i < size; i++) {
|
|
func(arr[i]); // Call the callback
|
|
}
|
|
}
|
|
|
|
void printDouble(int x) {
|
|
printf("%d ", x * 2);
|
|
}
|
|
|
|
void printSquare(int x) {
|
|
printf("%d ", x * x);
|
|
}
|
|
|
|
int main(void) {
|
|
int arr[] = {1, 2, 3, 4, 5};
|
|
|
|
processArray(arr, 5, printDouble); // 2 4 6 8 10
|
|
printf("\n");
|
|
processArray(arr, 5, printSquare); // 1 4 9 16 25
|
|
printf("\n");
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h:1037):
|
|
```c
|
|
typedef Clay_Dimensions (*Clay_MeasureTextFunction)(
|
|
Clay_String *text,
|
|
Clay_TextElementConfig *config,
|
|
void *userData
|
|
);
|
|
|
|
// Store in config
|
|
typedef struct {
|
|
Clay_MeasureTextFunction measureTextFunction;
|
|
void *userData;
|
|
} Clay_TextMeasureFunction;
|
|
|
|
// Usage
|
|
Clay_Dimensions measureText(
|
|
Clay_String *text,
|
|
Clay_TextElementConfig *config,
|
|
void *userData
|
|
) {
|
|
// Custom measurement
|
|
return (Clay_Dimensions){100, 20};
|
|
}
|
|
|
|
// Set callback
|
|
Clay_SetMeasureTextFunction(measureText, NULL);
|
|
```
|
|
|
|
### 12.4 Function Pointer Arrays
|
|
|
|
```c
|
|
#include <stdio.h>
|
|
|
|
int add(int a, int b) { return a + b; }
|
|
int sub(int a, int b) { return a - b; }
|
|
int mul(int a, int b) { return a * b; }
|
|
int div(int a, int b) { return a / b; }
|
|
|
|
int main(void) {
|
|
// Array of function pointers
|
|
int (*operations[4])(int, int) = {add, sub, mul, div};
|
|
|
|
printf("%d\n", operations[0](10, 5)); // 15 (add)
|
|
printf("%d\n", operations[1](10, 5)); // 5 (sub)
|
|
printf("%d\n", operations[2](10, 5)); // 50 (mul)
|
|
printf("%d\n", operations[3](10, 5)); // 2 (div)
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### 12.5 Callbacks with User Data
|
|
|
|
```c
|
|
typedef void (*Callback)(int value, void *userData);
|
|
|
|
void forEach(int *arr, int size, Callback func, void *userData) {
|
|
for (int i = 0; i < size; i++) {
|
|
func(arr[i], userData);
|
|
}
|
|
}
|
|
|
|
void printWithPrefix(int value, void *userData) {
|
|
char *prefix = (char*)userData;
|
|
printf("%s%d\n", prefix, value);
|
|
}
|
|
|
|
int main(void) {
|
|
int arr[] = {1, 2, 3};
|
|
forEach(arr, 3, printWithPrefix, "Number: ");
|
|
// Output:
|
|
// Number: 1
|
|
// Number: 2
|
|
// Number: 3
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
typedef void (*Clay_QueryScrollCallback)(
|
|
Clay_ScrollContainerData *scrollData,
|
|
void *userData
|
|
);
|
|
|
|
// Usage with user data
|
|
void handleScroll(Clay_ScrollContainerData *data, void *userData) {
|
|
MyContext *ctx = (MyContext*)userData;
|
|
// Use ctx...
|
|
}
|
|
|
|
Clay_SetQueryScrollCallback(handleScroll, &myContext);
|
|
```
|
|
|
|
### 12.6 Key Concepts Learned
|
|
- ✅ Function pointers store function addresses
|
|
- ✅ Typedef for cleaner function pointer syntax
|
|
- ✅ Callbacks for customizable behavior
|
|
- ✅ Function pointer arrays
|
|
- ✅ User data pattern for context
|
|
|
|
---
|
|
|
|
## Chapter 13: Building Complete Programs {#chapter-13}
|
|
|
|
### 13.1 Simple Clay Example
|
|
|
|
Let's build a complete UI program:
|
|
|
|
```c
|
|
// example.c
|
|
#define CLAY_IMPLEMENTATION
|
|
#include "clay.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
// Memory for Clay
|
|
Clay_Arena arena;
|
|
char arenaMemory[1024 * 1024]; // 1MB
|
|
|
|
// Text measurement (simplified)
|
|
Clay_Dimensions MeasureText(
|
|
Clay_String *text,
|
|
Clay_TextElementConfig *config,
|
|
void *userData
|
|
) {
|
|
// Simple: 10 pixels per character, 20 pixels high
|
|
return (Clay_Dimensions){
|
|
.width = text->length * 10,
|
|
.height = 20
|
|
};
|
|
}
|
|
|
|
int main(void) {
|
|
// Initialize Clay
|
|
arena.memory = arenaMemory;
|
|
arena.capacity = sizeof(arenaMemory);
|
|
|
|
Clay_Initialize(arena, (Clay_Dimensions){1024, 768});
|
|
Clay_SetMeasureTextFunction(MeasureText, NULL);
|
|
|
|
// Begin layout
|
|
Clay_BeginLayout();
|
|
|
|
// Create UI hierarchy
|
|
CLAY({
|
|
.layout = {
|
|
.sizing = {
|
|
.width = CLAY_SIZING_GROW(0),
|
|
.height = CLAY_SIZING_GROW(0)
|
|
},
|
|
.padding = CLAY_PADDING_ALL(16),
|
|
.childGap = 16,
|
|
.layoutDirection = CLAY_TOP_TO_BOTTOM
|
|
},
|
|
.backgroundColor = CLAY_COLOR(200, 200, 200, 255)
|
|
}) {
|
|
// Header
|
|
CLAY_TEXT(
|
|
CLAY_STRING("My Application"),
|
|
CLAY_TEXT_CONFIG({
|
|
.fontSize = 24,
|
|
.textColor = CLAY_COLOR(0, 0, 0, 255)
|
|
})
|
|
);
|
|
|
|
// Content
|
|
CLAY({
|
|
.layout = {
|
|
.sizing = {
|
|
.width = CLAY_SIZING_GROW(0),
|
|
.height = CLAY_SIZING_FIXED(200)
|
|
},
|
|
.padding = CLAY_PADDING_ALL(8)
|
|
},
|
|
.backgroundColor = CLAY_COLOR(255, 255, 255, 255)
|
|
}) {
|
|
CLAY_TEXT(
|
|
CLAY_STRING("This is my UI content!"),
|
|
CLAY_TEXT_CONFIG({
|
|
.fontSize = 16,
|
|
.textColor = CLAY_COLOR(0, 0, 0, 255)
|
|
})
|
|
);
|
|
}
|
|
}
|
|
|
|
// End layout and get render commands
|
|
Clay_RenderCommandArray commands = Clay_EndLayout();
|
|
|
|
// Render (simplified - just print)
|
|
printf("Generated %d render commands\n", commands.length);
|
|
for (int i = 0; i < commands.length; i++) {
|
|
Clay_RenderCommand *cmd = &commands.internalArray[i];
|
|
printf("Command %d: type=%d, bounds=(%.0f,%.0f,%.0f,%.0f)\n",
|
|
i, cmd->commandType,
|
|
cmd->boundingBox.x, cmd->boundingBox.y,
|
|
cmd->boundingBox.width, cmd->boundingBox.height
|
|
);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### 13.2 Compiling
|
|
|
|
```bash
|
|
# GCC
|
|
gcc -o example example.c -lm
|
|
|
|
# Clang
|
|
clang -o example example.c -lm
|
|
|
|
# With warnings
|
|
gcc -Wall -Wextra -o example example.c -lm
|
|
|
|
# With optimization
|
|
gcc -O2 -o example example.c -lm
|
|
```
|
|
|
|
### 13.3 Makefiles
|
|
|
|
Automate compilation:
|
|
|
|
```makefile
|
|
# Makefile
|
|
CC = gcc
|
|
CFLAGS = -Wall -Wextra -O2
|
|
LIBS = -lm
|
|
|
|
example: example.c clay.h
|
|
$(CC) $(CFLAGS) -o example example.c $(LIBS)
|
|
|
|
clean:
|
|
rm -f example
|
|
|
|
run: example
|
|
./example
|
|
```
|
|
|
|
Usage:
|
|
```bash
|
|
make # Build
|
|
make run # Build and run
|
|
make clean # Remove executable
|
|
```
|
|
|
|
### 13.4 Multi-File Project
|
|
|
|
```
|
|
project/
|
|
├── Makefile
|
|
├── clay.h
|
|
├── src/
|
|
│ ├── main.c
|
|
│ ├── ui.c
|
|
│ ├── ui.h
|
|
│ └── utils.c
|
|
│ └── utils.h
|
|
```
|
|
|
|
**ui.h:**
|
|
```c
|
|
#ifndef UI_H
|
|
#define UI_H
|
|
|
|
#include "clay.h"
|
|
|
|
void UI_CreateLayout(void);
|
|
|
|
#endif
|
|
```
|
|
|
|
**ui.c:**
|
|
```c
|
|
#include "ui.h"
|
|
|
|
void UI_CreateLayout(void) {
|
|
CLAY({
|
|
.layout = {
|
|
.sizing = {
|
|
.width = CLAY_SIZING_GROW(0),
|
|
.height = CLAY_SIZING_GROW(0)
|
|
}
|
|
}
|
|
}) {
|
|
// UI code
|
|
}
|
|
}
|
|
```
|
|
|
|
**main.c:**
|
|
```c
|
|
#define CLAY_IMPLEMENTATION
|
|
#include "clay.h"
|
|
#include "ui.h"
|
|
|
|
int main(void) {
|
|
// Initialize Clay
|
|
|
|
Clay_BeginLayout();
|
|
UI_CreateLayout();
|
|
Clay_RenderCommandArray commands = Clay_EndLayout();
|
|
|
|
// Render...
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### 13.5 Common C Patterns Recap
|
|
|
|
**1. Initialization Pattern:**
|
|
```c
|
|
typedef struct {
|
|
int *data;
|
|
size_t size;
|
|
} Array;
|
|
|
|
Array* Array_Create(size_t size) {
|
|
Array *arr = malloc(sizeof(Array));
|
|
arr->data = malloc(size * sizeof(int));
|
|
arr->size = size;
|
|
return arr;
|
|
}
|
|
|
|
void Array_Destroy(Array *arr) {
|
|
free(arr->data);
|
|
free(arr);
|
|
}
|
|
```
|
|
|
|
**2. Error Handling:**
|
|
```c
|
|
typedef enum {
|
|
STATUS_OK,
|
|
STATUS_ERROR_NULL_POINTER,
|
|
STATUS_ERROR_OUT_OF_MEMORY
|
|
} Status;
|
|
|
|
Status doSomething(void *data) {
|
|
if (data == NULL) {
|
|
return STATUS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
void *mem = malloc(100);
|
|
if (mem == NULL) {
|
|
return STATUS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
// Do work...
|
|
|
|
free(mem);
|
|
return STATUS_OK;
|
|
}
|
|
```
|
|
|
|
**3. Iterator Pattern:**
|
|
```c
|
|
typedef struct {
|
|
int *current;
|
|
int *end;
|
|
} Iterator;
|
|
|
|
Iterator Array_Begin(Array *arr) {
|
|
return (Iterator){arr->data, arr->data + arr->size};
|
|
}
|
|
|
|
bool Iterator_HasNext(Iterator *it) {
|
|
return it->current < it->end;
|
|
}
|
|
|
|
int Iterator_Next(Iterator *it) {
|
|
return *(it->current++);
|
|
}
|
|
|
|
// Usage
|
|
Iterator it = Array_Begin(&myArray);
|
|
while (Iterator_HasNext(&it)) {
|
|
int value = Iterator_Next(&it);
|
|
printf("%d\n", value);
|
|
}
|
|
```
|
|
|
|
### 13.6 Key Concepts Learned
|
|
- ✅ Complete program structure
|
|
- ✅ Compiling with gcc/clang
|
|
- ✅ Makefiles for automation
|
|
- ✅ Multi-file project organization
|
|
- ✅ Common C patterns
|
|
|
|
---
|
|
|
|
## Conclusion and Next Steps
|
|
|
|
### What You've Learned
|
|
|
|
You now understand:
|
|
|
|
1. **C Fundamentals**: Variables, types, functions, control flow
|
|
2. **Memory Management**: Pointers, stack vs heap, arena allocators
|
|
3. **Advanced Types**: Structs, unions, enums, typedefs
|
|
4. **Preprocessor**: Macros, conditional compilation, code generation
|
|
5. **Project Organization**: Headers, source files, compilation
|
|
6. **Real-World Patterns**: As demonstrated by Clay library
|
|
|
|
### Clay Demonstrates
|
|
|
|
- **Professional C code**: Clean, well-documented, performant
|
|
- **Single-header pattern**: Easy distribution and integration
|
|
- **Zero dependencies**: Portable, minimal C programming
|
|
- **Memory efficiency**: Arena allocators, no malloc in hot path
|
|
- **API design**: Clear, consistent, user-friendly
|
|
- **Macro DSL**: Declarative UI in C
|
|
|
|
### Continue Learning
|
|
|
|
**Read Clay Source:**
|
|
- `clay.h` - Study the implementation
|
|
- `examples/` - See real usage
|
|
- `renderers/` - Integration with graphics libraries
|
|
|
|
**Build Projects:**
|
|
- Simple calculator UI
|
|
- File browser interface
|
|
- Game menu system
|
|
- Settings panel
|
|
|
|
**Explore More:**
|
|
- SDL2/Raylib for graphics
|
|
- stb_image for image loading
|
|
- Other single-header libraries
|
|
|
|
**Advanced Topics:**
|
|
- SIMD optimization
|
|
- Platform-specific code
|
|
- Custom memory allocators
|
|
- Performance profiling
|
|
|
|
### Resources
|
|
|
|
- **Clay Website**: https://nicbarker.com/clay
|
|
- **C Reference**: https://en.cppreference.com/w/c
|
|
- **The C Programming Language** by Kernighan & Ritchie
|
|
- **Modern C** by Jens Gustedt
|
|
|
|
---
|
|
|
|
## Practice Exercises
|
|
|
|
### Exercise 1: Modify Clay Layout
|
|
Create a 3-column layout with different colors.
|
|
|
|
### Exercise 2: Custom Text Measurement
|
|
Implement proper text measurement using a font library.
|
|
|
|
### Exercise 3: Handle Input
|
|
Add mouse click detection to Clay elements.
|
|
|
|
### Exercise 4: Scrolling Container
|
|
Create a scrollable list of items.
|
|
|
|
### Exercise 5: Build a Calculator
|
|
Create a calculator UI using Clay.
|
|
|
|
---
|
|
|
|
**Happy Coding!** 🚀
|
|
|
|
You now have a solid foundation in C programming. Practice regularly, read professional C code like Clay, and build projects to reinforce your learning. |