C Refresher
Pointers & Memory
Pointers are the single most important C concept for embedded programming. ESP-IDF APIs use pointers everywhere.
The Basics of Pointers
A variable holds a value. A pointer is simply a variable that holds a memory address.
1#include <stdio.h>23int main() {4 int value = 42;5 int *ptr = &value; // ptr holds the ADDRESS of value (& means "address of")67 printf("Value: %d\n", value); // 428 9 // %p is the format specifier for printing pointer addresses10 printf("Address: %p\n", (void *)ptr); 11 12 // *ptr reads the value at the address (Dereferencing)13 printf("Dereference: %d\n", *ptr); // 42 1415 // Write through the pointer16 *ptr = 100; 17 printf("Value now: %d\n", value); // 100 — value was changed via pointer!1819 return 0;20}Desktop vs Microcontroller Memory
On a desktop computer running Windows or Linux, the operating system gives your program "Virtual Memory". The pointer address 0x7fffcb2a1 doesn't exist on the physical RAM chip; the OS fakes it.
On a microcontroller like the ESP32 running bare-metal or FreeRTOS, memory is physical. If you write to pointer address 0x6000403C, you aren't just changing a variable in RAM — you are physically sending electricity to the GPIO hardware register to turn on an LED! This is why pointers are so powerful (and dangerous) in embedded C.
Pointers and Arrays
Arrays and pointers are intimately connected in C. The name of an array "decays" into a pointer to its first element.
1#include <stdio.h>23int main() {4 int data[4] = {10, 20, 30, 40};56 // Array name decays to a pointer to the first element7 int *p = data; // exactly the same as &data[0]8 9 printf("%d\n", *p); // Prints 1010 11 // Pointer arithmetic: Add 1 to move to the next integer in memory12 printf("%d\n", *(p+1)); // Prints 20 13 14 // array[index] is just syntactical sugar for *(array + index)15 printf("%d\n", p[2]); // Prints 301617 return 0;18}Pointers to Structs
When passing a large struct to a function, you don't want to copy the whole thing. You pass a pointer to it instead. To access members of a struct via a pointer, you use the arrow -> operator.
1#include <stdio.h>23typedef struct {4 float temperature;5 float humidity;6} sensor_data_t;78int main() {9 sensor_data_t reading = {25.5, 60.0};10 sensor_data_t *ptr = &reading;1112 // Access struct members through pointer using ->13 printf("Temp: %.1f\n", ptr->temperature); // 25.514 printf("Humidity: %.1f\n", ptr->humidity); // 60.01516 // This is exactly equivalent to:17 printf("Temp: %.1f\n", (*ptr).temperature); // 25.51819 return 0;20}Double Pointers
Sometimes, a function needs to modify a pointer itself, not just the data it points to. This requires a pointer-to-a-pointer (Double Pointer).
This is heavily used in FreeRTOS and ESP-IDF when a function creates a resource and needs to return a "Handle" back to you.
1#include <stdio.h>23// Note: This is pseudo-code modeling a FreeRTOS Task Handle4typedef void* TaskHandle_t;56// The function takes a pointer to your handle so it can write to it7void create_task(TaskHandle_t *out_handle) {8 // Function creates a task internally and gets a handle9 TaskHandle_t internal_handle = (TaskHandle_t)0x12345678;10 11 // Function writes the handle back through the caller's pointer12 *out_handle = internal_handle; 13}1415int main() {16 TaskHandle_t my_task = NULL;17 18 // Pass the ADDRESS of my_task19 create_task(&my_task); 20 21 // my_task now contains the newly created handle22 printf("Task created with handle: %p\n", my_task);23 24 return 0;25}Function Pointers (Advanced)
Functions reside in memory just like variables do. This means you can create a pointer to a function. This is used in ESP-IDF for callbacks — event handlers and interrupt service routines.
1#include <stdio.h>23// 1. Define a function pointer type4// It must match the exact signature (return type and arguments)5typedef void (*event_handler_t)(int event_id);67// 2. A function that matches the signature8void wifi_event_handler(int event_id) {9 printf("WiFi event occurred: %d\n", event_id);10}1112// 3. A function that accepts a function pointer as an argument13void register_handler(event_handler_t handler) {14 // Store handler, and call it later when an event occurs15 printf("Registering handler...\n");16 handler(42); // Calling the function via the pointer17}1819int main() {20 // Usage: We pass the function name (which decays to a pointer)21 register_handler(wifi_event_handler);22 return 0;23}
