Table of Contents
KEY TAKEAWAYS
- The Strategy pattern encapsulates algorithms behind function pointers so they can be swapped
- The calling code is independent of the specific algorithm used
- Bundle related function pointers in a struct when the strategy has multiple operations
- This pattern is the foundation of C’s
qsort(),bsearch(), and many embedded driver APIs
What Is the Strategy Pattern?
The Strategy pattern lets you define a family of algorithms, put each one behind the same interface, and swap them at runtime. In C, this means using function pointers to select which algorithm runs without changing the code that calls it.Instead of hardcoding one approach, you make the algorithm a parameter. The calling code says what to do; the strategy decides how.Example 1: Sorting with Swappable Comparators
Bad — Hardcoded Sort Order
void sort_sensors_by_id(Sensor *arr, int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j arr[j+1].id) {
Sensor tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}
void sort_sensors_by_value(Sensor *arr, int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j arr[j+1].value) { // Only this line differs
Sensor tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}The entire sort function is duplicated — only the comparison line differs. Adding a third sort order means a third copy.Good — Comparison Strategy
typedef int (*CompareFunc)(const Sensor *a, const Sensor *b);
void sort_sensors(Sensor *arr, int n, CompareFunc cmp) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j 0) {
Sensor tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}
// Strategies
int compare_by_id(const Sensor *a, const Sensor *b) {
return a->id - b->id;
}
int compare_by_value(const Sensor *a, const Sensor *b) {
return (a->value > b->value) - (a->value value);
}
int compare_by_name(const Sensor *a, const Sensor *b) {
return strcmp(a->name, b->name);
}
// Usage — swap strategy without changing sort code
sort_sensors(sensors, count, compare_by_id);
sort_sensors(sensors, count, compare_by_value);
sort_sensors(sensors, count, compare_by_name);One sort function, multiple strategies. This is exactly how the C standard library’s qsort() works — it takes a comparator function pointer as a strategy.Example 2: Data Compression Strategy
Bad — Switch on Compression Type
int compress_data(const uint8_t *in, int in_len, uint8_t *out, int type) {
switch (type) {
case COMPRESS_RLE:
return rle_compress(in, in_len, out);
case COMPRESS_LZ:
return lz_compress(in, in_len, out);
case COMPRESS_HUFF:
return huffman_compress(in, in_len, out);
default:
return -1;
}
// New algorithm? Modify this function.
}Good — Strategy via Function Pointer
typedef int (*CompressFunc)(const uint8_t *in, int in_len, uint8_t *out);
typedef struct {
const char *name;
CompressFunc compress;
CompressFunc decompress;
} CompressionStrategy;
// Define strategies
static const CompressionStrategy strategies[] = {
{ "RLE", rle_compress, rle_decompress },
{ "LZ77", lz_compress, lz_decompress },
{ "Huffman", huffman_compress, huffman_decompress },
};
// Use a strategy
void store_data(const uint8_t *data, int len, const CompressionStrategy *s) {
uint8_t compressed[4096];
int clen = s->compress(data, len, compressed);
printf("Compressed with %s: %d -> %d bytesn", s->name, len, clen);
flash_write(compressed, clen);
}
// Caller picks the strategy
store_data(sensor_data, data_len, &strategies[0]); // RLE
store_data(sensor_data, data_len, &strategies[2]); // HuffmanAdding a new compression algorithm means adding one entry to the strategies array. The store_data function never changes. Each strategy bundles both compress and decompress functions together.Strategy Pattern vs. Function Pointers
Every strategy pattern uses function pointers, but not every function pointer is a strategy. The distinction:- Strategy: you have a family of interchangeable algorithms behind a common interface, and the caller selects which one to use
- Simple callback: you have one hook point where the caller provides custom behavior
compress + decompress), that is a strong signal you are using the Strategy pattern.When to Use the Strategy Pattern
- Multiple algorithms for the same task — sorting, compression, hashing, filtering
- Runtime selection — picking an algorithm based on configuration or input
- Testing — swapping real implementations with test doubles
- Avoiding switch/if chains that grow with each new variant
Key Takeaways
- The Strategy pattern encapsulates algorithms behind function pointers so they can be swapped
- The calling code is independent of the specific algorithm used
- Bundle related function pointers in a struct when the strategy has multiple operations
- This pattern is the foundation of C’s
qsort(),bsearch(), and many embedded driver APIs

Vivek Bhageria — Lead Firmware R&D Engineer, 12+ years. Ex-Bosch (automotive powertrain), MusicTribe (real-time audio), medical devices. M.Tech BITS Pilani. I write at NerdyElectronics — practical, register-level embedded systems for engineers who want to understand what’s actually happening under the hood.







