Advanced Pointers in C
Pointers are far more versatile than just storing the address of a variable. C allows pointers to point to functions, arrays, strings, and even other pointers. Understanding advanced pointer concepts is key to writing flexible, powerful C programs and understanding how things like callbacks, generic functions, and dynamic dispatch work.
Pointer to an Array
A pointer to an array points to the entire array as a unit, not just the first element.
#include <stdio.h>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr; // pointer to first element
int (*aptr)[5] = &arr; // pointer to the entire array of 5 ints
printf("Using ptr (element pointer):\n");
for (int i = 0; i < 5; i++)
printf("%d ", *(ptr + i));
printf("\nFirst element via array pointer: %d\n", (*aptr)[0]);
printf("Third element via array pointer: %d\n", (*aptr)[2]);
return 0;
}
Output
Using ptr (element pointer):
10 20 30 40 50
First element via array pointer: 10
Third element via array pointer: 30
Array of Pointers
An array where each element is a pointer. Very commonly used to hold an array of strings.
#include <stdio.h>
int main() {
// Array of pointer to string literals
char *fruits[] = {"Apple", "Banana", "Mango", "Orange"};
int n = 4;
for (int i = 0; i < n; i++)
printf("fruits[%d] = %s\n", i, fruits[i]);
return 0;
}
Output
fruits[0] = Apple
fruits[1] = Banana
fruits[2] = Mango
fruits[3] = Orange
Array of Pointers to Integers
#include <stdio.h>
int main() {
int a = 10, b = 20, c = 30;
int *ptrs[3] = {&a, &b, &c};
for (int i = 0; i < 3; i++)
printf("Value %d: %d\n", i + 1, *ptrs[i]);
return 0;
}
Output
Value 1: 10
Value 2: 20
Value 3: 30
Function Pointers
A function pointer stores the address of a function. Just like a variable pointer points to data, a function pointer points to executable code. This allows passing functions as arguments, building callback systems, and creating dispatch tables.
Syntax
return_type (*pointer_name)(parameter_types);
Basic Example
#include <stdio.h>
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
int main() {
int (*operate)(int, int); // function pointer declaration
operate = add;
printf("add(10, 3) = %d\n", operate(10, 3));
operate = sub;
printf("sub(10, 3) = %d\n", operate(10, 3));
operate = mul;
printf("mul(10, 3) = %d\n", operate(10, 3));
return 0;
}
Output
add(10, 3) = 13
sub(10, 3) = 7
mul(10, 3) = 30
Passing a Function Pointer to Another Function (Callback)
#include <stdio.h>
int square(int n) { return n * n; }
int doubleIt(int n){ return n * 2; }
void applyToAll(int arr[], int n, int (*func)(int)) {
for (int i = 0; i < n; i++)
printf("%d ", func(arr[i]));
printf("\n");
}
int main() {
int nums[] = {1, 2, 3, 4, 5};
printf("Squares : ");
applyToAll(nums, 5, square);
printf("Doubled : ");
applyToAll(nums, 5, doubleIt);
return 0;
}
Output
Squares : 1 4 9 16 25
Doubled : 2 4 6 8 10
Dispatch Table Using Function Pointer Array
#include <stdio.h>
float add(float a, float b) { return a + b; }
float sub(float a, float b) { return a - b; }
float mul(float a, float b) { return a * b; }
float dvd(float a, float b) { return (b != 0) ? a / b : 0; }
int main() {
float (*ops[4])(float, float) = {add, sub, mul, dvd};
char *names[] = {"+", "-", "*", "/"};
float x = 12, y = 4;
for (int i = 0; i < 4; i++)
printf("%.0f %s %.0f = %.2f\n", x, names[i], y, ops[i](x, y));
return 0;
}
Output
12 + 4 = 16.00
12 - 4 = 8.00
12 * 4 = 48.00
12 / 4 = 3.00
void Pointer (Generic Pointer)
A void * pointer is a generic pointer that can hold the address of any data type without type specification. It cannot be dereferenced directly — it must be cast to a specific type first. It is used when writing generic functions that work with any data type.
#include <stdio.h>
void printValue(void *ptr, char type) {
switch (type) {
case 'i': printf("Integer: %d\n", *(int *)ptr); break;
case 'f': printf("Float: %.2f\n", *(float *)ptr); break;
case 'c': printf("Char: %c\n", *(char *)ptr); break;
}
}
int main() {
int num = 42;
float price = 9.99;
char grade = 'A';
printValue(&num, 'i');
printValue(&price, 'f');
printValue(&grade, 'c');
return 0;
}
Output
Integer: 42
Float: 9.99
Char: A
const Pointers
The const keyword can be used with pointers in two ways:
1. Pointer to Constant — cannot change the value it points to
int value = 10;
const int *ptr = &value;
// *ptr = 20; // ERROR — cannot modify value through this pointer
ptr = &value; // OK — can change where pointer points
2. Constant Pointer — cannot change where it points
int a = 5, b = 10;
int * const ptr = &a;
*ptr = 99; // OK — can modify the value
// ptr = &b; // ERROR — cannot point to a different variable
3. Constant Pointer to Constant — cannot change either
const int * const ptr = &a;
// *ptr = 99; // ERROR
// ptr = &b; // ERROR
Pointer and String Operations
#include <stdio.h>
int strLen(const char *s) {
int count = 0;
while (*s != '\0') { count++; s++; }
return count;
}
void reverseStr(char *s) {
char *start = s;
char *end = s;
while (*end) end++;
end--;
while (start < end) {
char tmp = *start;
*start++ = *end;
*end-- = tmp;
}
}
int main() {
char word[] = "Pointer";
printf("Length : %d\n", strLen(word));
reverseStr(word);
printf("Reversed: %s\n", word);
return 0;
}
Output
Length : 7
Reversed: retniop
Common Pointer Pitfalls
| Pitfall | What Happens | Prevention |
|---|---|---|
| Wild Pointer | Uninitialized pointer used — undefined behavior | Always initialize: int *p = NULL; |
| NULL Dereference | Accessing value via NULL pointer — program crashes | Check if (ptr != NULL) before dereferencing |
| Dangling Pointer | Pointer still points to freed/out-of-scope memory | Set ptr = NULL after free(ptr) |
| Memory Leak | Allocated memory never freed — program slowly consumes RAM | Always call free() for every malloc() |
| Buffer Overflow | Writing past array bounds — corrupts memory | Always track array sizes and stay within bounds |
Summary of Pointer Types
| Type | Declaration | Use Case |
|---|---|---|
| Basic pointer | int *p | Point to a single variable |
| Pointer to array | int (*p)[5] | Point to a whole array |
| Array of pointers | int *p[5] | Array of addresses |
| Function pointer | int (*p)(int, int) | Point to a function |
| void pointer | void *p | Generic pointer, any type |
| Pointer to pointer | int **p | Point to another pointer |
| const pointer | const int *p | Read-only access via pointer |
Summary
Advanced pointer concepts unlock the real power of C. Pointers to arrays and arrays of pointers are the foundation of multi-dimensional data and string tables. Function pointers enable callback-driven designs and runtime polymorphism. Void pointers allow writing generic functions that work with any data type. Understanding these concepts deeply is what separates a C beginner from a proficient systems programmer.
