Pointers and Arrays
Introduction
One of the most important relationships in C programming is between pointers and arrays. In fact, arrays and pointers are closely related concepts, with arrays often being treated as pointers in many contexts. Understanding this relationship is crucial for effective C programming, as it enables efficient array manipulation, dynamic memory allocation, and implementation of complex data structures.
Array-Pointer Equivalence
Arrays as Pointers
In most contexts, an array name is treated as a pointer to its first element:
int arr[5] = {10, 20, 30, 40, 50};
// These are equivalent:
int *ptr1 = arr; // Array name decays to pointer
int *ptr2 = &arr[0]; // Explicit address of first element
printf("arr[0] = %d, *ptr1 = %d, *ptr2 = %d\n", arr[0], *ptr1, *ptr2);Important Distinction
While arrays and pointers are often interchangeable, they are not identical:
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;
// Arrays have fixed size
sizeof(arr); // Returns 20 bytes (5 * sizeof(int))
// Pointers have size of address
sizeof(ptr); // Returns 8 bytes on 64-bit system (size of pointer)
// Arrays cannot be reassigned
// arr = ptr; // Error: Cannot assign to array
// Pointers can be reassigned
ptr = &arr[2]; // OK: Pointer can be changedPointer Subscripting
Using Pointers as Arrays
Pointers can be used with array subscript notation:
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr;
// These are equivalent:
printf("arr[2] = %d\n", arr[2]);
printf("ptr[2] = %d\n", ptr[2]);
printf("*(arr + 2) = %d\n", *(arr + 2));
printf("*(ptr + 2) = %d\n", *(ptr + 2));Array Access Methods
There are multiple equivalent ways to access array elements:
int arr[5] = {10, 20, 30, 40, 50};
// Method 1: Array subscript notation
int value1 = arr[2];
// Method 2: Pointer arithmetic with dereference
int value2 = *(arr + 2);
// Method 3: Using pointer variable
int *ptr = arr;
int value3 = *(ptr + 2);
int value4 = ptr[2];
// All methods produce the same result
printf("%d %d %d %d\n", value1, value2, value3, value4); // 30 30 30 30Pointer Arithmetic with Arrays
Incrementing Pointers
Pointers can be incremented to traverse arrays:
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr;
// Traverse array using pointer arithmetic
for (int i = 0; i < 5; i++) {
printf("Element %d: %d\n", i, *ptr);
ptr++; // Move to next element
}
// Reset pointer to beginning
ptr = arr;
// Alternative traversal
for (int i = 0; i < 5; i++) {
printf("Element %d: %d\n", i, *(ptr + i));
}Pointer Difference
The difference between two pointers gives the number of elements between them:
int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *ptr1 = &arr[2];
int *ptr2 = &arr[8];
// Number of elements between ptr1 and ptr2
ptrdiff_t distance = ptr2 - ptr1; // 6
printf("Distance: %td\n", distance);
// Check if pointer is within array bounds
if (ptr1 >= arr && ptr1 < arr + 10) {
printf("ptr1 is within array bounds\n");
}Multi-dimensional Arrays and Pointers
Two-dimensional Arrays
Two-dimensional arrays can be accessed using various pointer techniques:
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// Method 1: Standard array notation
printf("matrix[1][2] = %d\n", matrix[1][2]); // 7
// Method 2: Pointer to array
int (*ptr_to_row)[4] = matrix; // Pointer to array of 4 integers
printf("ptr_to_row[1][2] = %d\n", ptr_to_row[1][2]); // 7
// Method 3: Pointer to first element
int *ptr_to_element = &matrix[0][0];
printf("*(ptr_to_element + 1*4 + 2) = %d\n", *(ptr_to_element + 1*4 + 2)); // 7Row-wise Traversal
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// Traverse row by row
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
// Using pointer arithmetic
int *ptr = &matrix[0][0];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", *ptr);
ptr++;
}
printf("\n");
}Arrays as Function Parameters
Array Decay to Pointers
When arrays are passed to functions, they decay to pointers:
// These function declarations are equivalent:
void process_array1(int arr[]);
void process_array2(int *arr);
void process_array3(int arr[10]); // Size is ignored
// Implementation
void process_array(int arr[], int size) {
// sizeof(arr) returns pointer size, not array size!
for (int i = 0; i < size; i++) {
printf("Element %d: %d\n", i, arr[i]);
// Equivalent to: printf("Element %d: %d\n", i, *(arr + i));
}
}
int main() {
int numbers[5] = {10, 20, 30, 40, 50};
process_array(numbers, 5); // Pass array and size
return 0;
}Multi-dimensional Array Parameters
For multi-dimensional arrays, all dimensions except the first must be specified:
// Function accepting 2D array - second dimension must be specified
void process_matrix(int matrix[][4], int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
}
// Alternative using pointer notation
void process_matrix_ptr(int (*matrix)[4], int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
}
int main() {
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
process_matrix(matrix, 3);
return 0;
}Dynamic Arrays with Pointers
Allocating Dynamic Arrays
Pointers enable dynamic array allocation:
#include <stdio.h>
#include <stdlib.h>
int main() {
int size;
printf("Enter array size: ");
scanf("%d", &size);
// Allocate dynamic array
int *arr = malloc(size * sizeof(int));
if (arr == NULL) {
printf("Memory allocation failed\n");
return 1;
}
// Initialize array
for (int i = 0; i < size; i++) {
arr[i] = i * i;
}
// Use array
for (int i = 0; i < size; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
// Free memory
free(arr);
arr = NULL; // Prevent dangling pointer
return 0;
}Resizing Dynamic Arrays
#include <stdio.h>
#include <stdlib.h>
int main() {
int *arr = malloc(5 * sizeof(int));
int size = 5;
// Initialize array
for (int i = 0; i < size; i++) {
arr[i] = i + 1;
}
printf("Original array: ");
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// Resize array
size = 10;
arr = realloc(arr, size * sizeof(int));
if (arr == NULL) {
printf("Memory reallocation failed\n");
return 1;
}
// Initialize new elements
for (int i = 5; i < size; i++) {
arr[i] = (i + 1) * 2;
}
printf("Resized array: ");
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// Free memory
free(arr);
return 0;
}Pointer Arrays vs. Arrays of Pointers
Array of Pointers
An array of pointers stores multiple pointer values:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
// Array of string pointers
char *strings[4] = {
"Hello",
"World",
"C Programming",
"Pointers"
};
// Print all strings
for (int i = 0; i < 4; i++) {
printf("String %d: %s\n", i, strings[i]);
}
// Dynamic string array
char **dynamic_strings = malloc(3 * sizeof(char*));
dynamic_strings[0] = malloc(6 * sizeof(char));
strcpy(dynamic_strings[0], "First");
dynamic_strings[1] = malloc(7 * sizeof(char));
strcpy(dynamic_strings[1], "Second");
dynamic_strings[2] = malloc(6 * sizeof(char));
strcpy(dynamic_strings[2], "Third");
// Print dynamic strings
for (int i = 0; i < 3; i++) {
printf("Dynamic string %d: %s\n", i, dynamic_strings[i]);
}
// Free dynamic memory
for (int i = 0; i < 3; i++) {
free(dynamic_strings[i]);
}
free(dynamic_strings);
return 0;
}Pointer to Array
A pointer to an array points to the entire array:
int arr[5] = {1, 2, 3, 4, 5};
// Pointer to array of 5 integers
int (*ptr_to_array)[5] = &arr;
// Access elements
printf("First element: %d\n", (*ptr_to_array)[0]);
printf("Third element: %d\n", (*ptr_to_array)[2]);
// Increment pointer (moves by entire array size)
ptr_to_array++; // Points to next array of 5 integersAdvanced Array-Pointer Techniques
Pointer Arithmetic for Multi-dimensional Arrays
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// Treat 2D array as 1D array
int *ptr = &matrix[0][0];
for (int i = 0; i < 12; i++) {
printf("%d ", ptr[i]);
if ((i + 1) % 4 == 0) printf("\n");
}Function Pointers for Array Operations
#include <stdio.h>
// Function pointer type for array operations
typedef int (*array_operation)(int);
// Array operation functions
int square(int x) { return x * x; }
int cube(int x) { return x * x * x; }
int double_value(int x) { return x * 2; }
// Apply operation to array
void apply_operation(int *arr, int size, array_operation op) {
for (int i = 0; i < size; i++) {
arr[i] = op(arr[i]);
}
}
int main() {
int numbers[5] = {1, 2, 3, 4, 5};
int size = 5;
printf("Original: ");
for (int i = 0; i < size; i++) {
printf("%d ", numbers[i]);
}
printf("\n");
// Apply square operation
apply_operation(numbers, size, square);
printf("Squared: ");
for (int i = 0; i < size; i++) {
printf("%d ", numbers[i]);
}
printf("\n");
return 0;
}Practical Examples
Matrix Transpose Using Pointers
#include <stdio.h>
void transpose_matrix(int *matrix, int *transpose, int rows, int cols) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
// matrix[i][j] becomes transpose[j][i]
*(transpose + j * rows + i) = *(matrix + i * cols + j);
}
}
}
int main() {
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
int transpose[4][3];
transpose_matrix(&matrix[0][0], &transpose[0][0], 3, 4);
printf("Original matrix:\n");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
printf("\nTransposed matrix:\n");
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", transpose[i][j]);
}
printf("\n");
}
return 0;
}String Array Sorting
#include <stdio.h>
#include <string.h>
void sort_strings(char *strings[], int count) {
for (int i = 0; i < count - 1; i++) {
for (int j = 0; j < count - i - 1; j++) {
if (strcmp(strings[j], strings[j + 1]) > 0) {
// Swap pointers
char *temp = strings[j];
strings[j] = strings[j + 1];
strings[j + 1] = temp;
}
}
}
}
int main() {
char *fruits[] = {"apple", "orange", "banana", "pear", "grape"};
int count = 5;
printf("Original order:\n");
for (int i = 0; i < count; i++) {
printf("%s ", fruits[i]);
}
printf("\n");
sort_strings(fruits, count);
printf("Sorted order:\n");
for (int i = 0; i < count; i++) {
printf("%s ", fruits[i]);
}
printf("\n");
return 0;
}Dynamic 2D Array
#include <stdio.h>
#include <stdlib.h>
int** create_2d_array(int rows, int cols) {
// Allocate array of row pointers
int **matrix = malloc(rows * sizeof(int*));
if (matrix == NULL) return NULL;
// Allocate each row
for (int i = 0; i < rows; i++) {
matrix[i] = malloc(cols * sizeof(int));
if (matrix[i] == NULL) {
// Clean up on failure
for (int j = 0; j < i; j++) {
free(matrix[j]);
}
free(matrix);
return NULL;
}
}
return matrix;
}
void free_2d_array(int **matrix, int rows) {
for (int i = 0; i < rows; i++) {
free(matrix[i]);
}
free(matrix);
}
int main() {
int rows = 3, cols = 4;
int **matrix = create_2d_array(rows, cols);
if (matrix == NULL) {
printf("Memory allocation failed\n");
return 1;
}
// Initialize matrix
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = i * cols + j;
}
}
// Print matrix
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
// Free memory
free_2d_array(matrix, rows);
return 0;
}Summary
The relationship between pointers and arrays is fundamental to C programming. Key points to remember:
- Array-Pointer Equivalence: Arrays decay to pointers in most contexts
- Pointer Subscripting: Pointers can use array notation and vice versa
- Pointer Arithmetic: Operations are scaled by data type size
- Function Parameters: Arrays pass as pointers to functions
- Dynamic Arrays: Pointers enable runtime array allocation
- Multi-dimensional Arrays: Various pointer techniques for 2D arrays
- Memory Management: Proper allocation and deallocation of dynamic arrays
Understanding this relationship enables efficient array manipulation, dynamic memory allocation, and implementation of complex data structures in C.