Function callbacks are one of the most powerful features in C programming, enabling dynamic behavior, event handling, and generic programming. A callback is a function that is passed as an argument to another function, allowing the called function to "call back" to the provided code when needed. This pattern is fundamental to GUI programming, asynchronous operations, generic algorithms, and many system libraries.
What Is a Callback?
A callback is simply a function pointer that you pass to another function. The receiving function can then invoke that function pointer at appropriate times—when an event occurs, when data is ready, or as part of an algorithm.
Basic Callback Pattern: Function A Function B (uses callback) (callback implementation) | | |---> register callback -----| | | | | |<--- invoke callback -------| | |
Function Pointer Basics
Before diving into callbacks, we need to understand function pointers:
#include <stdio.h> // A simple function int add(int a, int b) { return a + b; } int subtract(int a, int b) { return a - b; } int main() { // Declare a function pointer int (*operation)(int, int); // Point to add function operation = add; printf("add(5, 3) = %d\n", operation(5, 3)); // Point to subtract function operation = subtract; printf("subtract(5, 3) = %d\n", operation(5, 3)); return 0; } Simple Callback Example
#include <stdio.h> // Callback function types typedef void (*callback_t)(int); // Function that accepts a callback void processNumbers(int start, int end, callback_t callback) { for (int i = start; i <= end; i++) { callback(i); // Call back with each number } } // Callback implementations void printNumber(int n) { printf("%d ", n); } void printSquare(int n) { printf("%d ", n * n); } void printEvenOdd(int n) { if (n % 2 == 0) { printf("%d(even) ", n); } else { printf("%d(odd) ", n); } } int main() { printf("Numbers: "); processNumbers(1, 5, printNumber); printf("\n"); printf("Squares: "); processNumbers(1, 5, printSquare); printf("\n"); printf("Even/Odd: "); processNumbers(1, 5, printEvenOdd); printf("\n"); return 0; } Output:
Numbers: 1 2 3 4 5 Squares: 1 4 9 16 25 Even/Odd: 1(odd) 2(even) 3(odd) 4(even) 5(odd)
Callbacks with Parameters and Context
Often callbacks need access to additional context or state:
#include <stdio.h> #include <string.h> // Callback with context (void* for user data) typedef void (*callback_with_context_t)(int, void*); // Processing function with context passing void processWithContext(int start, int end, callback_with_context_t callback, void *context) { for (int i = start; i <= end; i++) { callback(i, context); } } // Callback that needs context void printWithPrefix(int n, void *context) { char *prefix = (char*)context; printf("%s%d ", prefix, n); } void accumulateSum(int n, void *context) { int *sum = (int*)context; *sum += n; } int main() { // Using prefix context char *prefix = "Num: "; processWithContext(1, 3, printWithPrefix, prefix); printf("\n"); // Using sum context int total = 0; processWithContext(1, 5, accumulateSum, &total); printf("Sum: %d\n", total); return 0; } Generic Algorithms with Callbacks
The C standard library's qsort is a classic example of callbacks:
#include <stdio.h> #include <stdlib.h> #include <string.h> // Comparison callbacks for qsort int compareInt(const void *a, const void *b) { return *(int*)a - *(int*)b; } int compareIntDesc(const void *a, const void *b) { return *(int*)b - *(int*)a; } int compareString(const void *a, const void *b) { return strcmp(*(const char**)a, *(const char**)b); } int compareStringLength(const void *a, const void *b) { return strlen(*(const char**)a) - strlen(*(const char**)b); } void printIntArray(int *arr, int size) { for (int i = 0; i < size; i++) { printf("%d ", arr[i]); } printf("\n"); } void printStringArray(char **arr, int size) { for (int i = 0; i < size; i++) { printf("%s ", arr[i]); } printf("\n"); } int main() { // Sorting integers int ints[] = {5, 2, 8, 1, 9, 3, 7, 4, 6}; int intCount = sizeof(ints) / sizeof(ints[0]); qsort(ints, intCount, sizeof(int), compareInt); printf("Sorted ascending: "); printIntArray(ints, intCount); qsort(ints, intCount, sizeof(int), compareIntDesc); printf("Sorted descending: "); printIntArray(ints, intCount); // Sorting strings char *strings[] = {"banana", "apple", "cherry", "date", "elderberry"}; int strCount = sizeof(strings) / sizeof(strings[0]); qsort(strings, strCount, sizeof(char*), compareString); printf("Sorted strings: "); printStringArray(strings, strCount); qsort(strings, strCount, sizeof(char*), compareStringLength); printf("Sorted by length: "); printStringArray(strings, strCount); return 0; } Event Handling System
Callbacks are perfect for event-driven programming:
#include <stdio.h> #include <stdlib.h> #include <string.h> // Event types typedef enum { EVENT_CLICK, EVENT_KEYPRESS, EVENT_MOUSE_MOVE, EVENT_TIMER } EventType; // Event structure typedef struct { EventType type; int x; int y; char key; void *data; } Event; // Callback function type typedef void (*EventHandler)(Event *event, void *user_data); // Listener structure typedef struct Listener { EventType type; EventHandler handler; void *user_data; struct Listener *next; } Listener; // Event dispatcher typedef struct { Listener *listeners[4]; // One list per event type } EventDispatcher; // Initialize dispatcher void initDispatcher(EventDispatcher *disp) { for (int i = 0; i < 4; i++) { disp->listeners[i] = NULL; } } // Register event listener void addListener(EventDispatcher *disp, EventType type, EventHandler handler, void *user_data) { Listener *newListener = malloc(sizeof(Listener)); newListener->type = type; newListener->handler = handler; newListener->user_data = user_data; newListener->next = disp->listeners[type]; disp->listeners[type] = newListener; } // Dispatch event to all listeners void dispatchEvent(EventDispatcher *disp, Event *event) { Listener *listener = disp->listeners[event->type]; while (listener) { listener->handler(event, listener->user_data); listener = listener->next; } } // Event handler implementations void onClick(Event *event, void *user_data) { printf("Click at (%d, %d) - %s\n", event->x, event->y, (char*)user_data); } void onKeyPress(Event *event, void *user_data) { printf("Key '%c' pressed - %s\n", event->key, (char*)user_data); } void onMouseMove(Event *event, void *user_data) { printf("Mouse moved to (%d, %d) - %s\n", event->x, event->y, (char*)user_data); } void onTimer(Event *event, void *user_data) { int *counter = (int*)user_data; (*counter)++; printf("Timer tick %d\n", *counter); } int main() { EventDispatcher disp; initDispatcher(&disp); // Register various listeners addListener(&disp, EVENT_CLICK, onClick, "Button1"); addListener(&disp, EVENT_CLICK, onClick, "Button2"); addListener(&disp, EVENT_KEYPRESS, onKeyPress, "Keyboard"); addListener(&disp, EVENT_MOUSE_MOVE, onMouseMove, "Mouse"); int timerCounter = 0; addListener(&disp, EVENT_TIMER, onTimer, &timerCounter); // Simulate events Event e1 = {EVENT_CLICK, 100, 200, 0, NULL}; dispatchEvent(&disp, &e1); Event e2 = {EVENT_KEYPRESS, 0, 0, 'A', NULL}; dispatchEvent(&disp, &e2); Event e3 = {EVENT_MOUSE_MOVE, 150, 250, 0, NULL}; dispatchEvent(&disp, &e3); Event e4 = {EVENT_TIMER, 0, 0, 0, NULL}; dispatchEvent(&disp, &e4); dispatchEvent(&disp, &e4); // Cleanup (simplified - would need to free all listeners) return 0; } Callback Chains and Pipelines
Creating a pipeline of operations using callbacks:
#include <stdio.h> #include <stdlib.h> // Pipeline stage definition typedef struct Stage { void (*process)(int *data, void *context); void *context; struct Stage *next; } Stage; // Pipeline structure typedef struct { Stage *head; Stage *tail; } Pipeline; // Create pipeline Pipeline* createPipeline() { Pipeline *p = malloc(sizeof(Pipeline)); p->head = p->tail = NULL; return p; } // Add stage to pipeline void addStage(Pipeline *p, void (*process)(int*, void*), void *context) { Stage *s = malloc(sizeof(Stage)); s->process = process; s->context = context; s->next = NULL; if (p->tail) { p->tail->next = s; p->tail = s; } else { p->head = p->tail = s; } } // Run data through pipeline void runPipeline(Pipeline *p, int *data) { Stage *current = p->head; while (current) { current->process(data, current->context); current = current->next; } } // Pipeline stage implementations void multiplyBy(int *data, void *context) { int factor = *(int*)context; *data *= factor; } void addValue(int *data, void *context) { int add = *(int*)context; *data += add; } void clamp(int *data, void *context) { int *limits = (int*)context; if (*data < limits[0]) *data = limits[0]; if (*data > limits[1]) *data = limits[1]; } void printValue(int *data, void *context) { char *prefix = (char*)context; printf("%s: %d\n", prefix, *data); } int main() { // Create pipeline Pipeline *p = createPipeline(); // Add stages int factor = 2; addStage(p, multiplyBy, &factor); int add = 10; addStage(p, addValue, &add); int limits[] = {0, 50}; addStage(p, clamp, limits); addStage(p, printValue, "Result"); // Run data through pipeline int data[] = {5, 15, 25, 35, 45}; for (int i = 0; i < 5; i++) { int value = data[i]; runPipeline(p, &value); } // Cleanup Stage *current = p->head; while (current) { Stage *temp = current; current = current->next; free(temp); } free(p); return 0; } Asynchronous Operations with Callbacks
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <time.h> // Asynchronous operation structure typedef struct AsyncOp { void (*callback)(int result, void *user_data); void *user_data; int delay; } AsyncOp; // Simulate async operation queue AsyncOp* createAsyncOp(void (*cb)(int, void*), void *data, int delay) { AsyncOp *op = malloc(sizeof(AsyncOp)); op->callback = cb; op->user_data = data; op->delay = delay; return op; } // Simulate async operation completion void completeAsyncOp(AsyncOp *op) { sleep(op->delay); // Simulate work int result = rand() % 100; // Simulate result op->callback(result, op->user_data); free(op); } // Callback implementations void onDataLoaded(int result, void *user_data) { char *filename = (char*)user_data; printf("Data loaded from %s: %d\n", filename, result); } void onNetworkRequest(int result, void *user_data) { char *url = (char*)user_data; printf("Network request to %s completed: %d\n", url, result); } void onCalculationComplete(int result, void *user_data) { int *input = (int*)user_data; printf("Calculation on %d gave result: %d\n", *input, result); } int main() { srand(time(NULL)); printf("Starting async operations...\n"); // Start multiple async operations AsyncOp *op1 = createAsyncOp(onDataLoaded, "data.txt", 1); AsyncOp *op2 = createAsyncOp(onNetworkRequest, "api.example.com", 2); int value = 42; AsyncOp *op3 = createAsyncOp(onCalculationComplete, &value, 1); // Simulate completion (in real code, this would be event-driven) completeAsyncOp(op1); completeAsyncOp(op2); completeAsyncOp(op3); printf("All operations completed\n"); return 0; } Callback Registry System
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_CALLBACKS 10 // Callback registry for different events typedef struct { char event_name[50]; void (*callback)(void*); void *user_data; } CallbackEntry; typedef struct { CallbackEntry entries[MAX_CALLBACKS]; int count; } CallbackRegistry; // Initialize registry void initRegistry(CallbackRegistry *reg) { reg->count = 0; } // Register callback int registerCallback(CallbackRegistry *reg, const char *event, void (*callback)(void*), void *user_data) { if (reg->count >= MAX_CALLBACKS) return -1; CallbackEntry *entry = ®->entries[reg->count++]; strcpy(entry->event_name, event); entry->callback = callback; entry->user_data = user_data; return 0; } // Trigger event void triggerEvent(CallbackRegistry *reg, const char *event) { printf("\n=== Event: %s ===\n", event); for (int i = 0; i < reg->count; i++) { if (strcmp(reg->entries[i].event_name, event) == 0) { reg->entries[i].callback(reg->entries[i].user_data); } } } // Callback implementations void onStartup(void *data) { char *app = (char*)data; printf("%s: Starting up...\n", app); } void onShutdown(void *data) { char *app = (char*)data; printf("%s: Shutting down...\n", app); } void onSave(void *data) { char *filename = (char*)data; printf("Saving to %s...\n", filename); } void onTimer(void *data) { int *count = (int*)data; (*count)++; printf("Timer tick %d\n", *count); } int main() { CallbackRegistry reg; initRegistry(®); // Register callbacks registerCallback(®, "startup", onStartup, "App1"); registerCallback(®, "startup", onStartup, "App2"); registerCallback(®, "shutdown", onShutdown, "App1"); registerCallback(®, "save", onSave, "data.txt"); int timerCount = 0; registerCallback(®, "timer", onTimer, &timerCount); // Trigger events triggerEvent(®, "startup"); triggerEvent(®, "timer"); triggerEvent(®, "timer"); triggerEvent(®, "save"); triggerEvent(®, "shutdown"); return 0; } Filter and Map with Callbacks
#include <stdio.h> #include <stdlib.h> #include <string.h> // Map function (apply callback to each element) void map(int *array, int size, int (*func)(int)) { for (int i = 0; i < size; i++) { array[i] = func(array[i]); } } // Filter function (keep elements where callback returns true) int filter(int *array, int size, int (*predicate)(int), int *result) { int count = 0; for (int i = 0; i < size; i++) { if (predicate(array[i])) { result[count++] = array[i]; } } return count; } // Reduce function (accumulate using callback) int reduce(int *array, int size, int (*func)(int, int), int initial) { int result = initial; for (int i = 0; i < size; i++) { result = func(result, array[i]); } return result; } // Map operations int square(int x) { return x * x; } int doubleIt(int x) { return x * 2; } int negate(int x) { return -x; } // Predicates for filter int isEven(int x) { return x % 2 == 0; } int isPositive(int x) { return x > 0; } int isGreaterThan10(int x) { return x > 10; } // Reduce operations int sum(int acc, int x) { return acc + x; } int max(int acc, int x) { return x > acc ? x : acc; } int product(int acc, int x) { return acc * x; } void printArray(int *arr, int size, const char *name) { printf("%s: ", name); for (int i = 0; i < size; i++) { printf("%d ", arr[i]); } printf("\n"); } int main() { int numbers[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int size = sizeof(numbers) / sizeof(numbers[0]); printArray(numbers, size, "Original"); // Map operations map(numbers, size, square); printArray(numbers, size, "After square"); map(numbers, size, doubleIt); printArray(numbers, size, "After double"); // Filter operations int filtered[10]; int filteredCount = filter(numbers, size, isEven, filtered); printArray(filtered, filteredCount, "Even numbers"); // Reduce operations int total = reduce(numbers, size, sum, 0); printf("Sum: %d\n", total); int maximum = reduce(numbers, size, max, numbers[0]); printf("Max: %d\n", maximum); return 0; } Callback with Multiple Parameters Using Structs
#include <stdio.h> #include <stdlib.h> // Parameter structure for complex callbacks typedef struct { int x; int y; char op; void *user_data; } CalcParams; // Callback type typedef void (*ComplexCallback)(CalcParams *params, int result); // Calculator function void calculate(int a, int b, char op, ComplexCallback callback, void *user_data) { int result; switch (op) { case '+': result = a + b; break; case '-': result = a - b; break; case '*': result = a * b; break; case '/': if (b != 0) result = a / b; else result = 0; break; default: result = 0; } CalcParams params = {a, b, op, user_data}; callback(¶ms, result); } // Callback implementations void logResult(CalcParams *params, int result) { printf("Calculation: %d %c %d = %d\n", params->x, params->op, params->y, result); } void storeResult(CalcParams *params, int result) { int *storage = (int*)params->user_data; *storage = result; printf("Stored result %d\n", result); } void formatResult(CalcParams *params, int result) { char *buffer = (char*)params->user_data; sprintf(buffer, "%d %c %d = %d", params->x, params->op, params->y, result); } int main() { // Simple logging callback calculate(10, 5, '+', logResult, NULL); calculate(10, 5, '-', logResult, NULL); calculate(10, 5, '*', logResult, NULL); // Store result in variable int stored; calculate(20, 4, '/', storeResult, &stored); printf("Stored value: %d\n", stored); // Format result into string char formatted[100]; calculate(15, 3, '*', formatResult, formatted); printf("Formatted: %s\n", formatted); return 0; } Callback Error Handling
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> // Error callback type typedef void (*ErrorCallback)(int error_code, const char *message, void *context); // Operation that might fail int riskyOperation(int input, ErrorCallback onError, void *context) { if (input < 0) { onError(EINVAL, "Input cannot be negative", context); return -1; } if (input > 100) { onError(ERANGE, "Input exceeds maximum", context); return -1; } if (input == 42) { onError(0, "Warning: 42 is special", context); // Warning, not error } return input * 2; } // Error handlers void logError(int code, const char *msg, void *context) { FILE *log = (FILE*)context; fprintf(log, "Error %d: %s\n", code, msg); } void printError(int code, const char *msg, void *context) { printf("ERROR: %s (code %d)\n", msg, code); } void countError(int code, const char *msg, void *context) { int *counter = (int*)context; (*counter)++; printf("Error %d occurred (total: %d)\n", code, *counter); } int main() { // Test with different error handlers FILE *log = fopen("errors.log", "w"); int result; result = riskyOperation(50, logError, log); printf("Result: %d\n\n", result); result = riskyOperation(-5, printError, NULL); printf("Result: %d\n\n", result); int errorCount = 0; result = riskyOperation(150, countError, &errorCount); printf("Result: %d\n", result); result = riskyOperation(-10, countError, &errorCount); printf("Result: %d\n", result); printf("Total errors: %d\n\n", errorCount); result = riskyOperation(42, printError, NULL); // Warning printf("Result: %d\n", result); fclose(log); return 0; } Callback Performance Considerations
#include <stdio.h> #include <stdlib.h> #include <time.h> // Direct function call int addDirect(int a, int b) { return a + b; } // Function to be called via callback int addOperation(int a, int b) { return a + b; } // Callback type typedef int (*binary_op_t)(int, int); // Function using callback int applyCallback(int a, int b, binary_op_t op) { return op(a, b); } #define ITERATIONS 10000000 int main() { clock_t start, end; volatile int result; // Prevent optimization // Benchmark direct call start = clock(); for (int i = 0; i < ITERATIONS; i++) { result = addDirect(i, i + 1); } end = clock(); double direct_time = ((double)(end - start)) / CLOCKS_PER_SEC; // Benchmark callback start = clock(); for (int i = 0; i < ITERATIONS; i++) { result = applyCallback(i, i + 1, addOperation); } end = clock(); double callback_time = ((double)(end - start)) / CLOCKS_PER_SEC; printf("Direct call: %.3f seconds\n", direct_time); printf("Callback: %.3f seconds\n", callback_time); printf("Overhead: %.2f%%\n", (callback_time - direct_time) / direct_time * 100); return 0; } Common Callback Patterns
1. Comparison Callbacks (like qsort)
typedef int (*comparator_t)(const void*, const void*);
2. Iteration Callbacks
typedef void (*iterator_t)(void *item, void *context);
3. Predicate Callbacks (filtering)
typedef int (*predicate_t)(const void *item);
4. Transformation Callbacks
typedef void (*transform_t)(void *input, void *output, void *context);
5. Cleanup/Destructor Callbacks
typedef void (*destructor_t)(void *data);
Best Practices
- Always check for NULL function pointers before calling
- Use typedef for callback types to improve readability
- Provide context/void* parameters to make callbacks stateful
- Document callback expectations (when called, what parameters mean)
- Consider error handling in callback chains
- Be mindful of performance (callback overhead vs. flexibility trade-off)
- Avoid deep callback nesting (callback hell)
- Use const for read-only parameters in callbacks
Common Pitfalls
// Pitfall 1: Not checking for NULL callback void badFunction(callback_t cb) { cb(42); // Crash if cb is NULL! } // Pitfall 2: Calling callback after freeing its context void* context = malloc(...); register_callback(callback, context); free(context); // Later, callback called with freed context! // Pitfall 3: Recursive callbacks causing stack overflow void recursive_callback(int n, void *cb) { if (n > 0) { ((void (*)(int, void*))cb)(n-1, cb); // Recursive call through callback } } // Pitfall 4: Type mismatches in callback signatures int (*int_cb)(int) = (int (*)(int))some_callback; // Dangerous cast! // Pitfall 5: Not handling callback errors Callback Design Patterns Cheat Sheet
| Pattern | Description | Use Case |
|---|---|---|
| Event Listener | Multiple callbacks for events | GUI, messaging |
| Strategy | Different algorithms via callbacks | Sorting, searching |
| Observer | Notify interested parties | State changes |
| Chain of Responsibility | Pipeline of callbacks | Data processing |
| Command | Encapsulate operation | Undo/redo |
| Visitor | Traverse structures | Tree/Graph operations |
Conclusion
Function callbacks are a fundamental building block of flexible C programming. They enable:
- Generic algorithms that work with any data type
- Event-driven programming for interactive applications
- Asynchronous operations without blocking
- Plugin architectures with runtime behavior customization
- Decoupled code with separation of concerns
Key principles to remember:
- Callbacks are just function pointers passed as arguments
- Always include a
void*context parameter for state - Document callback signatures clearly
- Handle errors appropriately in callback chains
- Consider performance implications in tight loops
Mastering callbacks opens up powerful programming paradigms and is essential for working with many C libraries and frameworks. They provide the flexibility and extensibility that makes C suitable for everything from embedded systems to large-scale applications.
Complete C Programming Guide + Compilers Collection
1. C srand() Function – Understanding Seed Initialization
https://macronepal.com/understanding-the-c-srand-function
Explains how srand() initializes the pseudo-random number generator in C by setting a seed value. Using the same seed produces the same sequence, while time(NULL) gives different results each run.
2. C rand() Function Mechanics and Limitations
https://macronepal.com/c-rand-function-mechanics-and-limitations
Explains how rand() generates pseudo-random numbers between 0 and RAND_MAX, its deterministic nature, and limitations for security use cases.
3. C log() Function
https://macronepal.com/c-log-function-2
Covers natural logarithm calculation using <math.h> and its applications.
4. Mastering Date and Time in C
https://macronepal.com/mastering-date-and-time-in-c
Explains <time.h> functions like time(), clock(), difftime(), and struct tm.
5. Mastering time_t Type in C
https://macronepal.com/mastering-the-c-time_t-type-for-time-management
Explains time representation as seconds since Unix epoch and conversion functions.
6. C exp() Function
https://macronepal.com/c-exp-function-mechanics-and-implementation
Explains exponential function exp(x) and its scientific applications.
7. C log() Function (Alternate Guide)
https://macronepal.com/c-log-function
Comparison of log() and log10() with usage examples.
8. C log10() Function
https://macronepal.com/mastering-the-log10-function-in-c
Explains base-10 logarithm for engineering and scientific applications.
9. C tan() Function
https://macronepal.com/understanding-the-c-tan-function
Explains tangent function and radian-based calculations.
10. Random Numbers in C (Secure vs Predictable)
https://macronepal.com/mastering-c-random-numbers-for-secure-and-predictable-applications
Explains difference between rand() and secure randomness methods.
11. Free Online C Compiler
https://macronepal.com/free-online-c-code-compiler-2
Browser-based compiler for testing C programs instantly.
C Functions, Arguments, Parameters & Flow
Mastering Functions in C – Complete Guide
https://macronepal.com/c/mastering-functions-in-c-a-complete-guide/
Covers function structure, modular programming, and real-world usage.
Function Arguments in C
https://macronepal.com/c-function-arguments/
Explains how arguments are passed and used in function calls.
Function Parameters in C
https://macronepal.com/c-function-parameters/
Explains defining inputs for functions and matching them with arguments.
Function Declarations in C
https://macronepal.com/c-function-declarations-syntax-rules-and-best-practices/
Covers prototypes, syntax rules, and best practices.
Function Calls in C
https://macronepal.com/understanding-function-calls-in-c-syntax-mechanics-and-best-practices/
Explains execution flow and parameter handling during function calls.
Void Functions in C
https://macronepal.com/understanding-void-functions-in-c-syntax-patterns-and-best-practices/
Explains functions that do not return values.
Return Values in C
https://macronepal.com/c-return-values-mechanics-types-and-best-practices/
Explains different return types and how functions return results.
Pass-by-Value in C
https://macronepal.com/aws/understanding-pass-by-value-in-c-mechanics-implications-and-best-practices/
Explains how copies of variables are passed into functions.
Pass-by-Reference in C
https://macronepal.com/c/understanding-pass-by-reference-in-c-pointers-semantics-and-safe-practices/
Explains using pointers to modify original variables.
C strstr() Function
https://macronepal.com/aws/c-strstr-function/
Explains substring search inside strings in C.
C Preprocessor & Macros
https://macronepal.com/mastering-c-variadic-macros-for-flexible-debugging/
https://macronepal.com/mastering-the-stdc-macro-in-c/
https://macronepal.com/c-time-macro-mechanics-and-usage/
https://macronepal.com/understanding-the-c-date-macro/
https://macronepal.com/c-file-type/
https://macronepal.com/mastering-c-line-macro-for-debugging-and-diagnostics/
https://macronepal.com/mastering-predefined-macros-in-c/
https://macronepal.com/c-error-directive-mechanics-and-usage/
https://macronepal.com/understanding-the-c-pragma-directive/
https://macronepal.com/c-include-directive/
C Structures, Memory, Scope & Linkage
https://macronepal.com/mastering-structures-in-c/
https://macronepal.com/c-structure-declaration-mechanics-and-usage/
https://macronepal.com/c-structure-initialization-mechanics-and-best-practices/
https://macronepal.com/mastering-c-structure-member-access-for-reliable-data-handling/
https://macronepal.com/c-nested-structures/
https://macronepal.com/mastering-arrays-of-structures-in-c/
https://macronepal.com/c-structure-pointers-mechanics-and-implementation/
https://macronepal.com/understanding-c-structure-parameter-passing-mechanics/
https://macronepal.com/mastering-c-returning-structures-for-efficient-data-flow/
https://macronepal.com/c-self-referential-structures/
https://macronepal.com/mastering-structure-alignment-in-c/
https://macronepal.com/c-structure-padding-mechanics-and-optimization/
https://macronepal.com/understanding-c-flexible-array-members-mechanics-and-usage/
https://macronepal.com/mastering-c-anonymous-structures-for-flattened-data-layouts/
https://macronepal.com/c-unions/
https://macronepal.com/mastering-c-name-mangling-and-symbol-decoration/
https://macronepal.com/c-no-linkage-mechanics-and-scope-isolation/
https://macronepal.com/understanding-c-internal-linkage-mechanics-and-architecture/
C Scope, Storage Classes & Typedef
https://macronepal.com/mastering-function-prototype-scope-in-c/
https://macronepal.com/c-function-scope-mechanics-and-visibility/
https://macronepal.com/understanding-c-file-scope-mechanics-and-architecture/
https://macronepal.com/mastering-c-scope-rules-for-predictable-name-resolution/
https://macronepal.com/c-scope-rules/
https://macronepal.com/mastering-c-register-storage-class-for-historical-context-and-modern-alternatives/
https://macronepal.com/mastering-_thread_local-in-c/
https://macronepal.com/c-extern-storage-class-mechanics-and-usage/
https://macronepal.com/understanding-the-c-static-storage-class-mechanics-and-usage/
https://macronepal.com/c-auto-storage-class/
https://macronepal.com/c-typedef-with-pointers/
Extra Articles
https://macronepal.com/13757-2/
https://macronepal.com/13748-2/
https://macronepal.com/13747-2/
https://macronepal.com/13746-2/
https://macronepal.com/13745-2/
https://macronepal.com/13708-2/
https://macronepal.com/13707-2/
https://macronepal.com/13702-2/
Online Compilers
https://macronepal.com/free-html-online-code-compiler/
https://macronepal.com/free-online-python-code-compiler/
https://macronepal.com/free-online-python2-code-compiler/
https://macronepal.com/free-online-java-code-compiler/
https://macronepal.com/free-online-javascript-code-compiler/
https://macronepal.com/free-online-node-js-code-compiler/
https://macronepal.com/free-online-c-code-compiler/
https://macronepal.com/free-online-c-code-compiler-2/
https://macronepal.com/free-online-c-code-compiler-3/
https://macronepal.com/free-online-php-code-compiler/
https://macronepal.com/free-online-ruby-code-compiler/
https://macronepal.com/free-online-perl-code-compiler/
https://macronepal.com/free-online-lua-code-compiler/
https://macronepal.com/free-online-tcl-code-compiler/
https://macronepal.com/free-online-groovy-code-compiler/
https://macronepal.com/free-online-j-shell-code-compiler/
https://macronepal.com/free-online-haskell-code-compiler/
https://macronepal.com/free-online-scala-code-compiler/
https://macronepal.com/free-online-common-lisp-code-compiler/
https://macronepal.com/free-online-d-code-compiler/
https://macronepal.com/free-online-ada-code-compiler/
https://macronepal.com/free-erlang-code-compiler/
https://macronepal.com/free-online-assembly-code-compiler/
