Pointer and Array Relationship in C: Complete Guide
Relationship Between Pointer and Array in C: A Comprehensive Tutorial
In C, arrays and pointers are intimately related. An array name acts like a constant pointer to its first element, and pointer arithmetic can be used to traverse array elements. Understanding this relationship is crucial for mastering C programming, especially for efficient memory access, function parameter passing, and multidimensional array manipulation.
This tutorial provides an in-depth exploration of the relationship between pointers and arrays in C: fundamental concepts, pointer arithmetic, array decay, multidimensional arrays, common pitfalls, and best practices.
Conceptual Introduction & Fundamentals
The Fundamental Relationship
In C, an array name represents the address of its first element. Therefore:
c
int arr[5] = {10, 20, 30, 40, 50}; arr // Address of first element (&arr[0]) &arr[0] // Same address *arr // Value of first element (10) arr[0] // Same value (10) Key points:
arris equivalent to&arr[0]*arris equivalent toarr[0]arr + iis equivalent to&arr[i]*(arr + i)is equivalent toarr[i]
Array Name vs. Pointer Variable
| Aspect | Array Name | Pointer Variable |
|---|---|---|
| Type | Constant pointer to first element | Variable pointer |
| Mutability | Cannot be reassigned | Can be reassigned |
sizeof | Returns total array size | Returns pointer size |
| Address | Fixed at declaration | Can point anywhere |
c
int arr[5] = {1, 2, 3, 4, 5}; int *ptr = arr; // OK: assign array name to pointer ptr = ptr + 1; // OK: pointer can be reassigned arr = arr + 1; // Error: array name is not assignable Array Decay
When an array is passed to a function, it decays to a pointer to its first element. The function receives a pointer, not the entire array.
c
void func(int arr[]) { // arr is actually int* here sizeof(arr); // Returns pointer size (8 on 64-bit), not array size } Why This Relationship Matters
- Efficient memory access: Pointer arithmetic provides fast array traversal.
- Function parameter passing: Arrays are passed by reference (pointer), not by value.
- Dynamic memory allocation:
mallocreturns pointers used like arrays. - Multidimensional arrays: Complex pointer arithmetic for 2D/3D arrays.
- Low-level programming: Direct memory manipulation.
Syntax & Core Mechanics
Accessing Array Elements with Pointers
c
#include <stdio.h> int main(void) { int arr[3] = {5, 10, 15}; /* Using array indexing */ printf("arr[0] = %d\n", arr[0]); printf("arr[1] = %d\n", arr[1]); printf("arr[2] = %d\n", arr[2]); /* Using pointer notation */ printf("*arr = %d\n", *arr); printf("*(arr + 1) = %d\n", *(arr + 1)); printf("*(arr + 2) = %d\n", *(arr + 2)); return 0; } Expected output:
text
arr[0] = 5 arr[1] = 10 arr[2] = 15 *arr = 5 *(arr + 1) = 10 *(arr + 2) = 15
Traversing Array Using Pointer Arithmetic
c
#include <stdio.h> int main(void) { int arr[5] = {1, 2, 3, 4, 5}; int n = sizeof(arr) / sizeof(arr[0]); /* Method 1: Using pointer indexing */ int *ptr = &arr[0]; for (int i = 0; i < n; i++) { printf("%d ", ptr[i]); } printf("\n"); /* Method 2: Using pointer arithmetic */ for (int *p = arr; p < arr + n; p++) { printf("%d ", *p); } printf("\n"); /* Method 3: Using array indexing */ for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; } Expected output:
text
1 2 3 4 5 1 2 3 4 5 1 2 3 4 5
Pointer Arithmetic with Arrays
Pointer arithmetic advances by the size of the pointed-to type:
c
#include <stdio.h> int main(void) { int arr[] = {10, 20, 30, 40, 50}; int *p = arr; printf("p = %p, *p = %d\n", (void*)p, *p); p++; // Move to next integer (adds sizeof(int) bytes) printf("p = %p, *p = %d\n", (void*)p, *p); p += 2; // Move forward 2 integers printf("p = %p, *p = %d\n", (void*)p, *p); p--; // Move back 1 integer printf("p = %p, *p = %d\n", (void*)p, *p); return 0; } Expected output (addresses will vary):
text
p = 0x7ffc1234, *p = 10 p = 0x7ffc1238, *p = 20 p = 0x7ffc1240, *p = 40 p = 0x7ffc123c, *p = 30
Array Decay in Functions
c
#include <stdio.h> void f1(int arr[3]) { printf("Size in f1: %zu bytes\n", sizeof(arr)); } void f2(int arr[]) { printf("Size in f2: %zu bytes\n", sizeof(arr)); } void f3(int *arr) { printf("Size in f3: %zu bytes\n", sizeof(arr)); } int main(void) { int arr[3] = {1, 2, 3}; printf("Size in main(): %zu bytes\n", sizeof(arr)); f1(arr); f2(arr); f3(arr); return 0; } Expected output (64-bit system):
text
Size in main(): 12 bytes Size in f1: 8 bytes Size in f2: 8 bytes Size in f3: 8 bytes
All three function parameter syntaxes are equivalent: they all receive a pointer.
Pointers and 2D Arrays
A 2D array is an array of arrays. In pointer notation:
arr[i][j]is equivalent to*(*(arr + i) + j)
c
#include <stdio.h> int main(void) { int arr[3][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }; /* Using array indexing */ for (int i = 0; i < 3; i++) { for (int j = 0; j < 4; j++) { printf("%d ", arr[i][j]); } printf("\n"); } /* Using pointer notation */ printf("\nUsing pointer notation:\n"); for (int i = 0; i < 3; i++) { for (int j = 0; j < 4; j++) { printf("%d ", *(*(arr + i) + j)); } printf("\n"); } return 0; } Expected output:
text
1 2 3 4 5 6 7 8 9 10 11 12 Using pointer notation: 1 2 3 4 5 6 7 8 9 10 11 12
Memory layout (row-major order):
text
arr[0]: 1 2 3 4 (addresses 5000-5012) arr[1]: 5 6 7 8 (addresses 5016-5028) arr[2]: 9 10 11 12 (addresses 5032-5044)
Pointers and 3D Arrays
c
#include <stdio.h> int main(void) { int arr[2][3][2] = { {{5, 10}, {6, 11}, {7, 12}}, {{20, 30}, {21, 31}, {22, 32}} }; /* Using pointer notation */ for (int i = 0; i < 2; i++) { for (int j = 0; j < 3; j++) { for (int k = 0; k < 2; k++) { printf("%d ", *(*(*(arr + i) + j) + k)); } printf("\n"); } printf("\n"); } return 0; } Expected output:
text
5 10 6 11 7 12 20 30 21 31 22 32
Full Practical Code Example with Explanation
This program demonstrates the relationship between pointers and arrays in a realistic scenario: a matrix operation system using pointer arithmetic.
Program: Matrix Operations with Pointers
c
/** * matrix_pointer_ops.c * Demonstrates pointer-array relationship with matrix operations. * Shows 1D, 2D, and 3D array access using pointers. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #define MAX_LINE_LEN 100 #define ROWS 3 #define COLS 4 /* Function prototypes */ void demonstrate1DPointerArray(void); void demonstrate2DPointerArray(void); void demonstrate3DPointerArray(void); void demonstrateArrayDecay(void); void printMatrix(int matrix[ROWS][COLS]); void transposeMatrix(int src[ROWS][COLS], int dst[COLS][ROWS]); int main(void) { int choice; char buffer[MAX_LINE_LEN]; printf("=== Matrix Operations with Pointers ===\n"); while (1) { printf("\n--- Menu ---\n"); printf("1. 1D Array and Pointer Relationship\n"); printf("2. 2D Array and Pointer Relationship\n"); printf("3. 3D Array and Pointer Relationship\n"); printf("4. Array Decay Demonstration\n"); printf("5. Matrix Transpose (Pointer Notation)\n"); printf("6. Exit\n"); printf("Enter choice: "); fflush(stdout); if (fgets(buffer, sizeof(buffer), stdin) == NULL) { fprintf(stderr, "Error reading choice.\n"); return EXIT_FAILURE; } choice = atoi(buffer); switch (choice) { case 1: demonstrate1DPointerArray(); break; case 2: demonstrate2DPointerArray(); break; case 3: demonstrate3DPointerArray(); break; case 4: demonstrateArrayDecay(); break; case 5: { int matrix[ROWS][COLS] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }; int transposed[COLS][ROWS]; printf("\nOriginal matrix:\n"); printMatrix(matrix); transposeMatrix(matrix, transposed); printf("Transposed matrix:\n"); for (int i = 0; i < COLS; i++) { for (int j = 0; j < ROWS; j++) { printf("%d ", transposed[i][j]); } printf("\n"); } break; } case 6: printf("Exiting.\n"); return EXIT_SUCCESS; default: printf("Invalid choice. Please try again.\n"); break; } } return EXIT_SUCCESS; } /* Demonstrates 1D array-pointer relationship */ void demonstrate1DPointerArray(void) { printf("\n=== 1D Array and Pointer ===\n"); int arr[] = {10, 20, 30, 40, 50}; int n = sizeof(arr) / sizeof(arr[0]); printf("Array: "); for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); printf("arr = %p\n", (void*)arr); printf("&arr[0] = %p\n", (void*)&arr[0]); printf("*arr = %d\n", *arr); printf("arr[0] = %d\n", arr[0]); printf("\nTraversal using pointer arithmetic:\n"); for (int *p = arr; p < arr + n; p++) { printf("%d ", *p); } printf("\n"); printf("\nElement access equivalence:\n"); for (int i = 0; i < n; i++) { printf("arr[%d] = %d, *(arr + %d) = %d\n", i, arr[i], i, *(arr + i)); } } /* Demonstrates 2D array-pointer relationship */ void demonstrate2DPointerArray(void) { printf("\n=== 2D Array and Pointer ===\n"); int matrix[ROWS][COLS] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }; printf("Matrix using array indexing:\n"); printMatrix(matrix); printf("\nMatrix using pointer notation:\n"); for (int i = 0; i < ROWS; i++) { for (int j = 0; j < COLS; j++) { printf("%d ", *(*(matrix + i) + j)); } printf("\n"); } printf("\nRow addresses:\n"); for (int i = 0; i < ROWS; i++) { printf("matrix[%d] = %p, *(matrix + %d) = %p\n", i, (void*)matrix[i], i, (void*)*(matrix + i)); } printf("\nElement addresses:\n"); for (int i = 0; i < ROWS; i++) { for (int j = 0; j < COLS; j++) { printf("&matrix[%d][%d] = %p\n", i, j, (void*)&matrix[i][j]); } } } /* Demonstrates 3D array-pointer relationship */ void demonstrate3DPointerArray(void) { printf("\n=== 3D Array and Pointer ===\n"); int arr[2][3][2] = { {{5, 10}, {6, 11}, {7, 12}}, {{20, 30}, {21, 31}, {22, 32}} }; printf("3D array using pointer notation:\n"); for (int i = 0; i < 2; i++) { for (int j = 0; j < 3; j++) { for (int k = 0; k < 2; k++) { printf("arr[%d][%d][%d] = %d (pointer: %d)\n", i, j, k, arr[i][j][k], *(*(*(arr + i) + j) + k)); } } } } /* Demonstrates array decay */ void demonstrateArrayDecay(void) { printf("\n=== Array Decay ===\n"); int arr[5] = {1, 2, 3, 4, 5}; printf("In main: sizeof(arr) = %zu bytes\n", sizeof(arr)); void showSize(int a[]) { printf("In function: sizeof(a) = %zu bytes (pointer size)\n", sizeof(a)); } showSize(arr); } /* Prints a matrix */ void printMatrix(int matrix[ROWS][COLS]) { for (int i = 0; i < ROWS; i++) { for (int j = 0; j < COLS; j++) { printf("%d ", matrix[i][j]); } printf("\n"); } } /* Transposes a matrix using pointer notation */ void transposeMatrix(int src[ROWS][COLS], int dst[COLS][ROWS]) { for (int i = 0; i < ROWS; i++) { for (int j = 0; j < COLS; j++) { *(*(dst + j) + i) = *(*(src + i) + j); } } } Explanation of Pointer-Array Usage
- 1D array: Shows
arrequals&arr[0],*arrequalsarr[0], and pointer arithmetic traversal. - 2D array: Demonstrates
*(*(arr + i) + j)notation for element access. - 3D array: Shows
*(*(*(arr + i) + j) + k)for three-dimensional access. - Array decay: Demonstrates
sizeofdifference between main and function. - Matrix transpose: Uses pointer notation for matrix manipulation.
Expected Output (sample run)
text
=== Matrix Operations with Pointers === --- Menu --- 1. 1D Array and Pointer Relationship 2. 2D Array and Pointer Relationship 3. 3D Array and Pointer Relationship 4. Array Decay Demonstration 5. Matrix Transpose (Pointer Notation) 6. Exit Enter choice: 1 === 1D Array and Pointer === Array: 10 20 30 40 50 arr = 0x7ffc1234 &arr[0] = 0x7ffc1234 *arr = 10 arr[0] = 10 Traversal using pointer arithmetic: 10 20 30 40 50 Element access equivalence: arr[0] = 10, *(arr + 0) = 10 arr[1] = 20, *(arr + 1) = 20 arr[2] = 30, *(arr + 2) = 30 arr[3] = 40, *(arr + 3) = 40 arr[4] = 50, *(arr + 4) = 50 --- Menu --- ... Enter choice: 4 === Array Decay === In main: sizeof(arr) = 20 bytes In function: sizeof(a) = 8 bytes (pointer size)
Common Pitfalls, Edge Cases & Memory Considerations
1. Array Name is Not Modifiable
Attempting to assign to an array name causes a compile error.
c
int arr[5] = {1, 2, 3, 4, 5}; arr = arr + 1; // Error: assignment to expression with array type Solution: Use a separate pointer variable: int *ptr = arr; ptr = ptr + 1;
2. sizeof on Array vs. Pointer
sizeof returns different sizes for arrays and pointers.
c
int arr[5]; int *ptr = arr; sizeof(arr); // 20 (5 * 4) sizeof(ptr); // 8 (pointer size)
Solution: Pass array size separately to functions.
3. Array Decay in Functions
Functions receiving arrays actually receive pointers, losing size information.
c
void func(int arr[10]) { sizeof(arr); // Pointer size, not array size } Solution: Always pass array size as a separate parameter.
4. Pointer Arithmetic Out of Bounds
Accessing beyond array bounds using pointer arithmetic is undefined behavior.
c
int arr[5] = {1, 2, 3, 4, 5}; int *p = arr + 5; // One past the end (valid) int value = *p; // BUG: dereferencing one past the end Solution: Ensure pointers stay within [arr, arr + n - 1] for dereferencing.
5. Negative Indexing with Pointers
Using negative indices with pointers can access memory before the array.
c
int arr[5] = {1, 2, 3, 4, 5}; int *p = arr + 2; printf("%d\n", p[-2]); // arr[0] = 1 (valid if in range) printf("%d\n", p[-3]); // BUG: before array start Solution: Ensure pointer arithmetic stays within array bounds.
6. Multidimensional Array vs. Pointer to Pointer
A 2D array is not the same as a pointer to pointer.
c
int arr[3][4]; // Contiguous memory: 12 integers int **ptr; // Pointer to pointer: different layout ptr = arr; // Type mismatch
Solution: Use correct pointer types: int (*ptr)[4] = arr;
7. Row-Major Order Assumption
C stores multidimensional arrays in row-major order. Assuming column-major causes bugs.
Solution: Always use row-major order when accessing elements sequentially.
8. sizeof on Function Parameter
Function parameters declared as arrays are actually pointers; sizeof gives pointer size.
c
void func(int arr[100]) { sizeof(arr); // 8 (pointer), not 400 } Solution: Pass size explicitly or use a sentinel value.
9. Pointer to Array vs. Pointer to First Element
These are different types:
c
int arr[5]; int *p1 = arr; // Pointer to int (first element) int (*p2)[5] = &arr; // Pointer to array of 5 ints
Solution: Use the correct pointer type for the context.
10. Memory Considerations
- Arrays are stored contiguously; pointer arithmetic relies on this.
- Pointer arithmetic advances by
sizeof(type)bytes. - Array decay means functions receive pointers, not copies.
- Large arrays passed to functions are not copied (efficient).
Summary & Best Practices
The relationship between pointers and arrays in C is fundamental. Arrays decay to pointers, and pointer arithmetic provides efficient array traversal.
Key Takeaways
- Array name equals
&arr[0](address of first element). arr[i]equals*(arr + i).- Arrays decay to pointers when passed to functions.
- Pointer arithmetic advances by the size of the pointed-to type.
- 2D arrays use row-major order;
arr[i][j]=*(*(arr + i) + j). - 3D arrays extend the pattern:
*(*(*(arr + i) + j) + k). - Array names are constant pointers; they cannot be reassigned.
Best Practices
- Use array indexing for readability; use pointer arithmetic for performance-critical code.
- Pass array size explicitly when passing arrays to functions.
- Use
constfor read-only array parameters to prevent accidental modification. - Be aware of array decay; don’t rely on
sizeofin functions. - Use
size_tfor array sizes and pointer arithmetic. - Ensure pointer arithmetic stays within array bounds.
- Use correct pointer types for multidimensional arrays.
- Document pointer arithmetic with comments for clarity.
- Prefer standard library functions (
memcpy,memmove) for bulk operations. - Test with edge cases: empty arrays, single element, multidimensional boundaries.
By mastering the pointer-array relationship, you’ll write efficient C code and understand how arrays work at the memory level.



