Pointers are C's most powerful and most dangerous feature. They let you manipulate memory directly, build efficient data structures, and interface with hardware. Today you master pointer arithmetic, dynamic allocation, and memory safety.
A pointer stores a memory address. 'int *p = &x' makes p point to x. '*p' dereferences (reads/writes the value at the address). Pointer arithmetic: p+1 advances by sizeof(*p) bytes, not 1 byte. An int pointer advances by 4; a char pointer by 1. 'int a[10]; int *p = a;' — p and a are equivalent here. Arrays are syntactic sugar for pointer arithmetic: a[i] is *(a+i).
malloc(n) allocates n bytes from the heap and returns a void*. Always check for NULL (allocation failure). free(ptr) returns memory to the heap. Rules: every malloc needs exactly one free; never free the same pointer twice (double-free is a security vulnerability); never use a pointer after freeing it (use-after-free). calloc(n, size) allocates and zeroes. realloc(ptr, new_size) resizes an allocation.
The most dangerous C bugs: buffer overflow (write past array end), buffer over-read (read past array end — Heartbleed was this), use-after-free, double-free, memory leak (allocate but never free), and null pointer dereference. Use Valgrind to detect all of these at runtime: 'valgrind --leak-check=full ./program'. AddressSanitizer (compile with -fsanitize=address) catches these at near-native speed.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Dynamic array: grow as needed
typedef struct {
int *data;
size_t len;
size_t cap;
} IntVec;
void vec_push(IntVec *v, int val) {
if (v->len == v->cap) {
v->cap = v->cap ? v->cap * 2 : 4;
v->data = realloc(v->data, v->cap * sizeof(int));
if (!v->data) { perror("realloc"); exit(1); }
}
v->data[v->len++] = val;
}
void vec_free(IntVec *v) {
free(v->data);
v->data = NULL;
v->len = v->cap = 0;
}
int main(void) {
IntVec v = {0};
for (int i = 0; i < 20; i++) vec_push(&v, i * i);
for (size_t i = 0; i < v.len; i++) printf("%d ", v.data[i]);
printf("\n");
vec_free(&v);
return 0;
}
Implement a hash table in C with separate chaining. Support: insert(key, value), lookup(key), delete(key), and free_table(). Handle collisions correctly and test with at least 1,000 key-value pairs.