Introduction to Array of Pointers
An array of pointers is a collection of pointer variables stored in contiguous memory locations. Each element in the array is a pointer that can point to data of a specific type. This powerful data structure is essential for managing strings, creating dynamic data structures, and implementing efficient algorithms.
Array of Pointers Architecture
Array of Pointers Memory Layout ┌─────────────────────────────────────┐ │ Array of Pointers │ ├─────────────────────────────────────┤ │ ptr[0] ────────> Data block 0 │ │ ptr[1] ────────> Data block 1 │ │ ptr[2] ────────> Data block 2 │ │ ptr[3] ────────> Data block 3 │ │ ptr[4] ────────> Data block 4 │ └─────────────────────────────────────┘ Each pointer: 4 or 8 bytes (depending on architecture) Data blocks: Can be anywhere in memory (heap, stack, data segment)
Basic Array of Pointers
Declaration and Initialization
#include <stdio.h>
int main() {
// Array of integer pointers
int *ptr_arr[5]; // Array of 5 integer pointers
int a = 10, b = 20, c = 30, d = 40, e = 50;
// Assign addresses to pointers
ptr_arr[0] = &a;
ptr_arr[1] = &b;
ptr_arr[2] = &c;
ptr_arr[3] = &d;
ptr_arr[4] = &e;
// Access values through pointers
printf("Array of pointers:\n");
for (int i = 0; i < 5; i++) {
printf("ptr_arr[%d] points to value: %d\n", i, *ptr_arr[i]);
}
// Modify values through pointers
*ptr_arr[2] = 100;
printf("\nAfter modification:\n");
printf("c = %d (modified through pointer)\n", c);
return 0;
}
Output:
Array of pointers: ptr_arr[0] points to value: 10 ptr_arr[1] points to value: 20 ptr_arr[2] points to value: 30 ptr_arr[3] points to value: 40 ptr_arr[4] points to value: 50 After modification: c = 100 (modified through pointer)
Array of Pointers to Strings
1. String Arrays (Most Common Use)
#include <stdio.h>
int main() {
// Array of pointers to strings (string literals)
const char *weekdays[] = {
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday"
};
int num_days = sizeof(weekdays) / sizeof(weekdays[0]);
printf("Days of the week:\n");
for (int i = 0; i < num_days; i++) {
printf(" %d: %s (at address %p)\n",
i + 1, weekdays[i], (void*)weekdays[i]);
}
printf("\nSize of array: %zu bytes\n", sizeof(weekdays));
printf("Number of elements: %d\n", num_days);
printf("Size of each pointer: %zu bytes\n", sizeof(weekdays[0]));
return 0;
}
Output:
Days of the week: 1: Monday (at address 0x4006f8) 2: Tuesday (at address 0x4006ff) 3: Wednesday (at address 0x400707) 4: Thursday (at address 0x400711) 5: Friday (at address 0x40071a) 6: Saturday (at address 0x400721) 7: Sunday (at address 0x40072a) Size of array: 56 bytes Number of elements: 7 Size of each pointer: 8 bytes
2. Modifiable String Arrays
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
// Array of pointers to mutable strings (heap allocated)
char *names[5];
// Allocate memory and copy strings
names[0] = strdup("Alice");
names[1] = strdup("Bob");
names[2] = strdup("Charlie");
names[3] = strdup("David");
names[4] = strdup("Eve");
printf("Original names:\n");
for (int i = 0; i < 5; i++) {
printf(" names[%d] = %s (at %p)\n", i, names[i], (void*)names[i]);
}
// Modify a name
strcpy(names[2], "Charles");
printf("\nAfter modification:\n");
printf(" names[2] = %s\n", names[2]);
// Swap pointers
char *temp = names[0];
names[0] = names[4];
names[4] = temp;
printf("\nAfter swapping:\n");
for (int i = 0; i < 5; i++) {
printf(" names[%d] = %s\n", i, names[i]);
}
// Free allocated memory
for (int i = 0; i < 5; i++) {
free(names[i]);
}
return 0;
}
3. 2D Character Array vs Array of Pointers
#include <stdio.h>
#include <string.h>
int main() {
// Method 1: 2D character array (fixed size, contiguous)
char names2d[5][20] = {
"Alice",
"Bob",
"Charlie",
"David",
"Eve"
};
// Method 2: Array of pointers (flexible, non-contiguous)
const char *namesPtr[] = {
"Alice",
"Bob",
"Charlie",
"David",
"Eve"
};
printf("=== Memory Comparison ===\n\n");
printf("2D Array:\n");
printf(" Size of names2d: %zu bytes\n", sizeof(names2d));
printf(" Memory layout: Contiguous\n");
printf(" Can modify strings: Yes\n");
printf(" Wastes space for short strings\n");
printf("\nArray of Pointers:\n");
printf(" Size of namesPtr: %zu bytes\n", sizeof(namesPtr));
printf(" Memory layout: Pointers array (contiguous) + String literals (read-only)\n");
printf(" Can modify strings: No (string literals are read-only)\n");
printf(" Efficient for variable-length strings\n");
// Memory layout demonstration
printf("\n=== Memory Addresses ===\n");
printf("2D Array addresses:\n");
for (int i = 0; i < 5; i++) {
printf(" names2d[%d] at %p\n", i, (void*)names2d[i]);
}
printf("\nArray of Pointers addresses:\n");
for (int i = 0; i < 5; i++) {
printf(" namesPtr[%d] at %p (points to %p)\n",
i, (void*)&namesPtr[i], (void*)namesPtr[i]);
}
return 0;
}
Dynamic Array of Pointers
1. Creating Dynamic Array of Pointers
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int num_strings = 5;
// Allocate array of pointers
char **strings = (char**)malloc(num_strings * sizeof(char*));
if (strings == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// Allocate and initialize each string
strings[0] = strdup("Dynamic");
strings[1] = strdup("Array");
strings[2] = strdup("of");
strings[3] = strdup("Pointers");
strings[4] = strdup("Example");
printf("Dynamic array of pointers:\n");
for (int i = 0; i < num_strings; i++) {
printf(" strings[%d] = %s\n", i, strings[i]);
}
// Add a new string (resize array)
num_strings++;
strings = (char**)realloc(strings, num_strings * sizeof(char*));
strings[5] = strdup("New Element");
printf("\nAfter adding new element:\n");
for (int i = 0; i < num_strings; i++) {
printf(" strings[%d] = %s\n", i, strings[i]);
}
// Free memory
for (int i = 0; i < num_strings; i++) {
free(strings[i]);
}
free(strings);
return 0;
}
2. Dynamic 2D Array Using Array of Pointers
#include <stdio.h>
#include <stdlib.h>
// Function to create a dynamic 2D array
int** create_2d_array(int rows, int cols) {
// Allocate array of row pointers
int **matrix = (int**)malloc(rows * sizeof(int*));
if (matrix == NULL) {
return NULL;
}
// Allocate each row
for (int i = 0; i < rows; i++) {
matrix[i] = (int*)malloc(cols * sizeof(int));
if (matrix[i] == NULL) {
// Clean up previously allocated rows
for (int j = 0; j < i; j++) {
free(matrix[j]);
}
free(matrix);
return NULL;
}
}
return matrix;
}
// Function to free dynamic 2D array
void free_2d_array(int **matrix, int rows) {
for (int i = 0; i < rows; i++) {
free(matrix[i]);
}
free(matrix);
}
// Function to initialize matrix
void init_matrix(int **matrix, int rows, int cols) {
int value = 1;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = value++;
}
}
}
// Function to print matrix
void print_matrix(int **matrix, int rows, int cols) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%4d ", matrix[i][j]);
}
printf("\n");
}
}
int main() {
int rows = 4, cols = 5;
int **matrix = create_2d_array(rows, cols);
if (matrix == NULL) {
printf("Failed to create matrix\n");
return 1;
}
init_matrix(matrix, rows, cols);
printf("Dynamic 2D Array (%d x %d):\n", rows, cols);
print_matrix(matrix, rows, cols);
// Memory layout demonstration
printf("\nMemory layout:\n");
printf("matrix (pointer to pointers): %p\n", (void*)matrix);
for (int i = 0; i < rows; i++) {
printf(" matrix[%d] (row %d): %p\n", i, i, (void*)matrix[i]);
}
free_2d_array(matrix, rows);
return 0;
}
Array of Function Pointers
1. Simple Function Pointer Array
#include <stdio.h>
// Function prototypes
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
int divide(int a, int b) { return b != 0 ? a / b : 0; }
int modulus(int a, int b) { return b != 0 ? a % b : 0; }
int main() {
// Array of function pointers
int (*operations[])(int, int) = {
add, subtract, multiply, divide, modulus
};
const char *op_names[] = {
"Addition", "Subtraction", "Multiplication", "Division", "Modulus"
};
int a = 20, b = 5;
printf("Operations on %d and %d:\n", a, b);
printf("================================\n");
for (int i = 0; i < 5; i++) {
int result = operations[i](a, b);
printf("%-15s: %d\n", op_names[i], result);
}
// Calling through pointer
printf("\nDirect call through array:\n");
int (*func_ptr)(int, int) = operations[2]; // multiply
printf("20 * 5 = %d\n", func_ptr(20, 5));
return 0;
}
2. Menu System with Function Pointers
#include <stdio.h>
#include <stdlib.h>
// Menu functions
void new_game() { printf("Starting new game...\n"); }
void load_game() { printf("Loading game...\n"); }
void options() { printf("Options menu...\n"); }
void highscores() { printf("High scores...\n"); }
void quit() { printf("Goodbye!\n"); exit(0); }
typedef struct {
const char *name;
void (*function)();
} MenuItem;
int main() {
MenuItem main_menu[] = {
{"New Game", new_game},
{"Load Game", load_game},
{"Options", options},
{"High Scores", highscores},
{"Quit", quit}
};
int num_items = sizeof(main_menu) / sizeof(main_menu[0]);
int choice;
while (1) {
printf("\n=== MAIN MENU ===\n");
for (int i = 0; i < num_items; i++) {
printf("%d. %s\n", i + 1, main_menu[i].name);
}
printf("Enter choice: ");
scanf("%d", &choice);
if (choice >= 1 && choice <= num_items) {
main_menu[choice - 1].function();
} else {
printf("Invalid choice!\n");
}
}
return 0;
}
Array of Pointers to Structures
1. Basic Structure Array of Pointers
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char name[50];
int age;
float salary;
} Employee;
int main() {
int num_employees = 3;
// Array of pointers to Employee structures
Employee *employees[3];
// Allocate and initialize each employee
for (int i = 0; i < num_employees; i++) {
employees[i] = (Employee*)malloc(sizeof(Employee));
if (employees[i] == NULL) {
printf("Memory allocation failed\n");
return 1;
}
}
// Initialize data
strcpy(employees[0]->name, "Alice Smith");
employees[0]->age = 30;
employees[0]->salary = 50000.0;
strcpy(employees[1]->name, "Bob Johnson");
employees[1]->age = 35;
employees[1]->salary = 60000.0;
strcpy(employees[2]->name, "Charlie Brown");
employees[2]->age = 28;
employees[2]->salary = 45000.0;
// Print employee data
printf("Employee Records:\n");
printf("========================================\n");
for (int i = 0; i < num_employees; i++) {
printf("Employee %d:\n", i + 1);
printf(" Name: %s\n", employees[i]->name);
printf(" Age: %d\n", employees[i]->age);
printf(" Salary: %.2f\n", employees[i]->salary);
printf("\n");
}
// Free memory
for (int i = 0; i < num_employees; i++) {
free(employees[i]);
}
return 0;
}
2. Dynamic Array of Structure Pointers
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int id;
char title[100];
float price;
} Book;
// Function to add a book
Book** add_book(Book **library, int *count, int *capacity) {
if (*count >= *capacity) {
*capacity *= 2;
library = (Book**)realloc(library, *capacity * sizeof(Book*));
if (library == NULL) {
printf("Reallocation failed\n");
return NULL;
}
}
library[*count] = (Book*)malloc(sizeof(Book));
if (library[*count] == NULL) {
printf("Book allocation failed\n");
return library;
}
(*count)++;
return library;
}
// Function to free library
void free_library(Book **library, int count) {
for (int i = 0; i < count; i++) {
free(library[i]);
}
free(library);
}
// Function to print library
void print_library(Book **library, int count) {
printf("\nLibrary Collection:\n");
printf("====================\n");
for (int i = 0; i < count; i++) {
printf("ID: %d, Title: %s, Price: $%.2f\n",
library[i]->id, library[i]->title, library[i]->price);
}
}
int main() {
int count = 0;
int capacity = 2;
// Initial allocation
Book **library = (Book**)malloc(capacity * sizeof(Book*));
if (library == NULL) {
printf("Initial allocation failed\n");
return 1;
}
// Add books
library = add_book(library, &count, &capacity);
library[count-1]->id = 1;
strcpy(library[count-1]->title, "The C Programming Language");
library[count-1]->price = 45.50;
library = add_book(library, &count, &capacity);
library[count-1]->id = 2;
strcpy(library[count-1]->title, "Clean Code");
library[count-1]->price = 38.75;
library = add_book(library, &count, &capacity);
library[count-1]->id = 3;
strcpy(library[count-1]->title, "Design Patterns");
library[count-1]->price = 52.25;
print_library(library, count);
printf("\nLibrary statistics:\n");
printf(" Number of books: %d\n", count);
printf(" Current capacity: %d\n", capacity);
free_library(library, count);
return 0;
}
Sorting Arrays of Pointers
1. Sorting String Pointers
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// Comparison function for qsort
int compare_strings(const void *a, const void *b) {
// a and b are pointers to elements in the array
// Each element is a pointer to char (char*)
const char **str1 = (const char **)a;
const char **str2 = (const char **)b;
return strcmp(*str1, *str2);
}
int compare_strings_desc(const void *a, const void *b) {
const char **str1 = (const char **)a;
const char **str2 = (const char **)b;
return strcmp(*str2, *str1);
}
int compare_length(const void *a, const void *b) {
const char **str1 = (const char **)a;
const char **str2 = (const char **)b;
return strlen(*str1) - strlen(*str2);
}
int main() {
const char *fruits[] = {
"banana",
"apple",
"orange",
"grape",
"kiwi",
"strawberry"
};
int n = sizeof(fruits) / sizeof(fruits[0]);
printf("Original array:\n");
for (int i = 0; i < n; i++) {
printf(" %s\n", fruits[i]);
}
// Sort alphabetically
qsort(fruits, n, sizeof(char*), compare_strings);
printf("\nAlphabetically sorted:\n");
for (int i = 0; i < n; i++) {
printf(" %s\n", fruits[i]);
}
// Sort reverse alphabetically
qsort(fruits, n, sizeof(char*), compare_strings_desc);
printf("\nReverse alphabetically sorted:\n");
for (int i = 0; i < n; i++) {
printf(" %s\n", fruits[i]);
}
// Sort by length
qsort(fruits, n, sizeof(char*), compare_length);
printf("\nSorted by length:\n");
for (int i = 0; i < n; i++) {
printf(" %s (length %zu)\n", fruits[i], strlen(fruits[i]));
}
return 0;
}
2. Sorting Structure Pointers
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char name[50];
int age;
} Person;
// Comparison functions
int compare_by_name(const void *a, const void *b) {
const Person **p1 = (const Person **)a;
const Person **p2 = (const Person **)b;
return strcmp((*p1)->name, (*p2)->name);
}
int compare_by_age(const void *a, const void *b) {
const Person **p1 = (const Person **)a;
const Person **p2 = (const Person **)b;
return (*p1)->age - (*p2)->age;
}
int compare_by_age_desc(const void *a, const void *b) {
const Person **p1 = (const Person **)a;
const Person **p2 = (const Person **)b;
return (*p2)->age - (*p1)->age;
}
int main() {
// Create array of Person structures
Person people[] = {
{"Alice", 30},
{"Bob", 25},
{"Charlie", 35},
{"David", 28},
{"Eve", 32}
};
int n = sizeof(people) / sizeof(people[0]);
// Create array of pointers to Person
Person *person_ptrs[n];
for (int i = 0; i < n; i++) {
person_ptrs[i] = &people[i];
}
printf("Original order:\n");
for (int i = 0; i < n; i++) {
printf(" %s (%d)\n", person_ptrs[i]->name, person_ptrs[i]->age);
}
// Sort by name
qsort(person_ptrs, n, sizeof(Person*), compare_by_name);
printf("\nSorted by name:\n");
for (int i = 0; i < n; i++) {
printf(" %s (%d)\n", person_ptrs[i]->name, person_ptrs[i]->age);
}
// Sort by age
qsort(person_ptrs, n, sizeof(Person*), compare_by_age);
printf("\nSorted by age (ascending):\n");
for (int i = 0; i < n; i++) {
printf(" %s (%d)\n", person_ptrs[i]->name, person_ptrs[i]->age);
}
// Sort by age descending
qsort(person_ptrs, n, sizeof(Person*), compare_by_age_desc);
printf("\nSorted by age (descending):\n");
for (int i = 0; i < n; i++) {
printf(" %s (%d)\n", person_ptrs[i]->name, person_ptrs[i]->age);
}
return 0;
}
Command Line Arguments (argv)
#include <stdio.h>
int main(int argc, char *argv[]) {
// argv is an array of pointers to strings
// argc is the number of arguments
printf("Program name: %s\n", argv[0]);
printf("Number of arguments: %d\n", argc);
if (argc > 1) {
printf("\nCommand line arguments:\n");
for (int i = 1; i < argc; i++) {
printf(" argv[%d] = %s (at address %p)\n",
i, argv[i], (void*)argv[i]);
}
} else {
printf("\nNo additional arguments provided.\n");
}
// Demonstrate pointer arithmetic with argv
printf("\nUsing pointer arithmetic:\n");
char **arg_ptr = argv;
for (int i = 0; i < argc; i++) {
printf(" arg_ptr[%d] = %s\n", i, *(arg_ptr + i));
}
return 0;
}
Compile and run:
./program hello world 123 test
Output:
Program name: ./program Number of arguments: 4 Command line arguments: argv[1] = hello (at address 0x7ffc8a3b5a81) argv[2] = world (at address 0x7ffc8a3b5a87) argv[3] = 123 (at address 0x7ffc8a3b5a8d) Using pointer arithmetic: arg_ptr[0] = ./program arg_ptr[1] = hello arg_ptr[2] = world arg_ptr[3] = 123
Array of Pointers to Pointers
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// Array of pointers to pointers (char***)
char ***data = (char***)malloc(3 * sizeof(char**));
for (int i = 0; i < 3; i++) {
data[i] = (char**)malloc(4 * sizeof(char*));
for (int j = 0; j < 4; j++) {
data[i][j] = (char*)malloc(20 * sizeof(char));
sprintf(data[i][j], "data[%d][%d]", i, j);
}
}
printf("3D data structure:\n");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%s ", data[i][j]);
}
printf("\n");
}
// Free memory (reverse order)
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
free(data[i][j]);
}
free(data[i]);
}
free(data);
return 0;
}
Common Patterns and Techniques
1. Jagged Arrays (Rows of Different Lengths)
#include <stdio.h>
#include <stdlib.h>
int main() {
// Create a jagged array (rows with different column counts)
int **jagged = (int**)malloc(4 * sizeof(int*));
// Row lengths
int row_lengths[] = {3, 5, 2, 4};
// Allocate and initialize each row
for (int i = 0; i < 4; i++) {
jagged[i] = (int*)malloc(row_lengths[i] * sizeof(int));
for (int j = 0; j < row_lengths[i]; j++) {
jagged[i][j] = (i + 1) * 10 + j;
}
}
// Print jagged array
printf("Jagged Array:\n");
for (int i = 0; i < 4; i++) {
printf("Row %d (length %d): ", i, row_lengths[i]);
for (int j = 0; j < row_lengths[i]; j++) {
printf("%d ", jagged[i][j]);
}
printf("\n");
}
// Free memory
for (int i = 0; i < 4; i++) {
free(jagged[i]);
}
free(jagged);
return 0;
}
2. Lookup Tables
#include <stdio.h>
#include <string.h>
typedef struct {
const char *name;
int value;
} LookupEntry;
// Lookup function using array of pointers
LookupEntry* find_entry(const char *name, LookupEntry *table[], int size) {
for (int i = 0; i < size; i++) {
if (strcmp(table[i]->name, name) == 0) {
return table[i];
}
}
return NULL;
}
int main() {
// Create lookup entries
LookupEntry entries[] = {
{"RED", 0xFF0000},
{"GREEN", 0x00FF00},
{"BLUE", 0x0000FF},
{"WHITE", 0xFFFFFF},
{"BLACK", 0x000000}
};
int num_entries = sizeof(entries) / sizeof(entries[0]);
// Create array of pointers to entries
LookupEntry *lookup_table[num_entries];
for (int i = 0; i < num_entries; i++) {
lookup_table[i] = &entries[i];
}
// Test lookups
const char *tests[] = {"GREEN", "YELLOW", "RED", "PURPLE"};
for (int i = 0; i < 4; i++) {
LookupEntry *found = find_entry(tests[i], lookup_table, num_entries);
if (found) {
printf("%s: 0x%06X\n", found->name, found->value);
} else {
printf("%s: not found\n", tests[i]);
}
}
return 0;
}
Performance Considerations
Memory Access Patterns
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define SIZE 1000
int main() {
// Create two similar data structures
int array2d[SIZE][SIZE]; // 2D array (contiguous)
int **ptr_array; // Array of pointers
// Allocate pointer array
ptr_array = (int**)malloc(SIZE * sizeof(int*));
for (int i = 0; i < SIZE; i++) {
ptr_array[i] = (int*)malloc(SIZE * sizeof(int));
}
// Initialize
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
array2d[i][j] = i * j;
ptr_array[i][j] = i * j;
}
}
clock_t start, end;
long long sum = 0;
// Access 2D array (contiguous)
start = clock();
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
sum += array2d[i][j];
}
}
end = clock();
printf("2D array access time: %ld ticks\n", end - start);
sum = 0;
// Access pointer array (non-contiguous)
start = clock();
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
sum += ptr_array[i][j];
}
}
end = clock();
printf("Pointer array access time: %ld ticks\n", end - start);
// Cleanup
for (int i = 0; i < SIZE; i++) {
free(ptr_array[i]);
}
free(ptr_array);
return 0;
}
Best Practices Summary
Do's and Don'ts
// DO: Initialize all pointers
int *arr[5];
for (int i = 0; i < 5; i++) {
arr[i] = NULL; // Initialize to NULL
}
// DO: Check for NULL before dereferencing
if (arr[i] != NULL) {
printf("%d", *arr[i]);
}
// DO: Free allocated memory
for (int i = 0; i < count; i++) {
free(ptr_array[i]);
}
free(ptr_array);
// DO: Use const for read-only pointer arrays
const char *days[] = {"Mon", "Tue", "Wed"};
// DON'T: Forget to allocate memory for pointers
char *names[5];
// strcpy(names[0], "Alice"); // Undefined behavior! No memory allocated
// DON'T: Mix pointer types
int *int_ptr;
char **char_ptr_ptr;
// char_ptr_ptr = &int_ptr; // Type mismatch
// DON'T: Access out of bounds
int *arr[5];
// arr[5] = &x; // Array index out of bounds
Memory Management Rules
// Rule 1: Allocate in reverse order of usage
char ***data = malloc(x * sizeof(char**));
for (int i = 0; i < x; i++) {
data[i] = malloc(y * sizeof(char*));
for (int j = 0; j < y; j++) {
data[i][j] = malloc(z);
}
}
// Rule 2: Free in reverse order of allocation
for (int i = 0; i < x; i++) {
for (int j = 0; j < y; j++) {
free(data[i][j]);
}
free(data[i]);
}
free(data);
// Rule 3: Always check allocation success
char **ptr_array = malloc(n * sizeof(char*));
if (ptr_array == NULL) {
// Handle error
}
// Rule 4: Set freed pointers to NULL
free(ptr);
ptr = NULL;
Common Applications
| Application | Description | Example |
|---|---|---|
| String arrays | Store multiple strings | char *fruits[] = {"apple", "banana"}; |
| Command line args | Program arguments | int main(int argc, char *argv[]) |
| Dynamic 2D arrays | Matrices with variable row sizes | int **matrix = create_matrix(rows, cols); |
| Lookup tables | Fast data retrieval | LookupEntry *table[size]; |
| Function tables | Callback systems | void (*handlers[])(void) = {func1, func2}; |
| Jagged arrays | Rows of different lengths | int **jagged = create_jagged(row_lengths); |
| Object arrays | Collections of structures | Person *people[100]; |
Conclusion
Arrays of pointers are a versatile and powerful feature in C:
Key Takeaways
- Flexibility: Each pointer can point to different memory locations
- Efficiency: Only pointers are stored in the array, data can be anywhere
- Dynamic sizing: Can grow and shrink as needed
- Multiple dimensions: Create complex data structures
- Sorting: Easy to sort by swapping pointers instead of data
When to Use
- String arrays with variable lengths
- Dynamic 2D arrays (jagged arrays)
- Sorting collections efficiently
- Lookup tables and dictionaries
- Command line argument processing
- Function pointer tables for callbacks
- Collections of objects (polymorphism)
Advantages over 2D Arrays
- Memory efficient for variable-length data
- Faster sorting (swap pointers, not data)
- Dynamic resizing of individual rows
- Non-contiguous memory layout (flexible)
Arrays of pointers are essential for many advanced C programming patterns and are widely used in systems programming, application development, and algorithm implementation.