All Types of Pointers in Embedded C with
Examples
1. Basic Pointer to Variable
int sensor_value = 25;
int *ptr = &sensor_value; // Pointer to integer
// Access
*ptr = 30; // sensor_value now = 30
2. Pointer to Constant Data
const uint8_t ROM_DATA[] = {0xAA, 0xBB}; // Stored in Flash
const uint8_t *rom_ptr = ROM_DATA; // Read-only pointer
// Access
uint8_t first_byte = rom_ptr[0]; // OK: 0xAA
// rom_ptr[0] = 0xCC; // ERROR: Const violation
3. Constant Pointer (Fixed Address)
uint8_t buffer[64];
uint8_t *const buf_ptr = buffer; // Pointer can't change
// Access
buf_ptr[0] = 0xFF; // OK: Modify data
// buf_ptr = &other_buffer; // ERROR: Pointer const
4. Volatile Pointer (Hardware Access)
volatile uint32_t *const GPIOA_ODR = (volatile uint32_t*)0x40020014;
// Toggle output
*GPIOA_ODR ^= (1 << 5); // Atomic PA5 toggle
5. Pointer to Function
void led_on(void) { /* ... */ }
void led_off(void) { /* ... */ }
// Function pointer type
typedef void (*led_func_t)(void);
// Usage
led_func_t led_operation = led_on;
led_operation(); // Calls led_on()
6. Pointer to Array
uint8_t frame[8] = {0};
uint8_t (*frame_ptr)[8] = &frame; // Pointer to array
// Access
(*frame_ptr)[0] = 0x01; // frame[0] = 0x01
7. Array of Pointers
const char *error_messages[] = {
"Sensor Fail",
"Comm Error",
"Low Voltage"
};
// Access
uart_send(error_messages[1]); // Sends "Comm Error"
8. Pointer to Structure
typedef struct {
uint32_t cr;
uint32_t sr;
} UART_Regs;
UART_Regs *const UART1 = (UART_Regs*)0x40011000;
UART1->cr |= UART_ENABLE; // Set enable bit
9. Void Pointer (Generic)
uint32_t sensor = 25;
void *generic_ptr = &sensor;
// Must cast before use
uint32_t value = *(uint32_t*)generic_ptr;
10. Double Pointer (Pointer-to-Pointer)
uint8_t *dma_buffer;
uint8_t **dma_ptr = &dma_buffer; // Pointer to pointer
// Configure DMA
* DMA_CMAR = (uint32_t)*dma_ptr; // Set buffer address
11. Far Pointer (Segmented Architectures)
__far uint8_t *ext_mem_ptr = (__far uint8_t*)0x80000000; // Extended memory
12. Near Pointer (Local Memory)
__near uint8_t *ram_ptr = (__near uint8_t*)0x0000; // On-chip RAM
13. Null Pointer
uint32_t *ptr = NULL; // Explicit null pointer
if(ptr == NULL) {
// Handle uninitialized pointer
}
14. Wild Pointer (Uninitialized)
uint32_t *wild_ptr; // Dangerous uninitialized pointer
// *wild_ptr = 10; // UNDEFINED BEHAVIOR!
15. Dangling Pointer (Invalid Memory)
uint32_t *create_dangling() {
uint32_t local = 42;
return &local; // Returns stack address
} // 'local' destroyed here
uint32_t *dangler = create_dangling();
// *dangler = 10; // CORRUPTS MEMORY
16. Function Pointer with Parameters
int32_t (*sensor_read)(uint8_t sensor_id); // Function pointer
// Assign implementation
sensor_read = &read_temperature_sensor;
int32_t temp = sensor_read(SENSOR1); // Call function
17. Const Volatile Pointer
const volatile uint32_t *const RTC_COUNTER =
(const volatile uint32_t*)0x40002800; // Read-only hardware counter
uint32_t timestamp = *RTC_COUNTER; // Always reads actual value
Embedded-Specific Use Cases
1. Memory-Mapped I/O
// GPIO port definition
#define GPIOB_BASE 0x40020400
volatile GPIO_TypeDef *const GPIOB = (volatile GPIO_TypeDef*)GPIOB_BASE;
// Set PB7 high
GPIOB->ODR |= (1 << 7);
2. DMA Buffer Handling
__attribute__((aligned(4))) uint8_t dma_buf[128];
uint8_t *dma_ptr = dma_buf;
// Configure DMA source
DMA1->CMAR = (uint32_t)dma_ptr;
3. ISR Callback Table
// Array of function pointers
void (*isr_handlers[8])(void) = {0};
// Register UART1 handler
isr_handlers[USART1_IRQn] = &uart1_isr;
// Dispatch in vector table
void USART1_IRQHandler() {
if(isr_handlers[USART1_IRQn])
isr_handlers[USART1_IRQn]();
}
4. Command Parser
typedef struct {
const char *cmd;
void (*handler)(void);
} CmdEntry;
const CmdEntry cmd_table[] = {
{"LED_ON", &led_on},
{"READ", &read_sensor}
};
void parse_command(const char *cmd) {
for(int i=0; i<sizeof(cmd_table)/sizeof(CmdEntry); i++) {
if(strcmp(cmd, cmd_table[i].cmd) == 0) {
cmd_table[i].handler();
return;
}
}
}
Pointer Safety in Embedded Systems
1. Always initialize pointers
uint32_t *ptr = NULL; // Safe initialization
2. Use const for hardware registers
volatile const uint32_t *ADC_DATA = ...; // Read-only
3. Validate pointers before use
if(ptr >= MEM_START && ptr < MEM_END) {
*ptr = value;
}
4. Avoid pointer arithmetic with hardware registers
// BAD: TIM1_CR1 + 0x04 (risky)
// GOOD: Use structure access
TIM1->CCR1 = value;
5. Use compiler attributes for alignment
__attribute__((aligned(4))) uint32_t buffer[64];
Pointers in embedded C enable direct hardware access and efficient memory manipulation
but require careful management to avoid crashes and security vulnerabilities.