🧭 Navigation
⬅️ Previous | 🏠 Home | ➡️ Next
Pointers are one of the most powerful and fundamental concepts in C programming. They allow you to directly manipulate memory addresses and create efficient, flexible programs.
A pointer is a variable that stores the memory address of another variable. Instead of storing a value directly, a pointer stores the location where a value is stored in memory.
int *ptr; // Pointer to an integer
char *cptr; // Pointer to a character
float *fptr; // Pointer to a float
double *dptr; // Pointer to a doubleThe & operator returns the memory address of a variable:
int number = 42;
int *ptr = &number; // ptr now contains the address of numberThe * operator is used to access the value stored at the address pointed to by a pointer:
int number = 42;
int *ptr = &number;
printf("Value: %d\n", *ptr); // Prints: Value: 42#include <stdio.h>
int main() {
int x = 10;
int *ptr = &x;
printf("Value of x: %d\n", x);
printf("Address of x: %p\n", (void*)&x);
printf("Value of ptr: %p\n", (void*)ptr);
printf("Value pointed by ptr: %d\n", *ptr);
return 0;
}#include <stdio.h>
int main() {
int number = 100;
int *ptr = &number;
printf("Before: number = %d\n", number);
*ptr = 200; // Modify the value through the pointer
printf("After: number = %d\n", number);
return 0;
}A null pointer is a pointer that doesn't point to any valid memory location. It's represented by NULL:
int *ptr = NULL; // Initialize pointer to NULL
if (ptr == NULL) {
printf("Pointer is NULL\n");
}Pointers support arithmetic operations:
#include <stdio.h>
int main() {
int arr[] = {10, 20, 30, 40, 50};
int *ptr = arr; // Points to first element
printf("First element: %d\n", *ptr);
printf("Second element: %d\n", *(ptr + 1));
printf("Third element: %d\n", *(ptr + 2));
return 0;
}Arrays and pointers are closely related in C. In fact, an array name is essentially a pointer to the first element of the array.
#include <stdio.h>
int main() {
int arr[] = {1, 2, 3, 4, 5};
// arr is equivalent to &arr[0]
printf("arr: %p\n", (void*)arr);
printf("&arr[0]: %p\n", (void*)&arr[0]);
// Both are the same
if (arr == &arr[0]) {
printf("arr and &arr[0] are equal!\n");
}
return 0;
}#include <stdio.h>
int main() {
int arr[] = {10, 20, 30, 40, 50};
int *ptr = arr;
printf("=== Array Notation ===\n");
for (int i = 0; i < 5; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
printf("\n=== Pointer Notation ===\n");
for (int i = 0; i < 5; i++) {
printf("*(ptr + %d) = %d\n", i, *(ptr + i));
}
printf("\n=== Alternative Pointer Notation ===\n");
for (int i = 0; i < 5; i++) {
printf("ptr[%d] = %d\n", i, ptr[i]);
}
return 0;
}#include <stdio.h>
int main() {
int arr[] = {100, 200, 300, 400, 500};
int *ptr = arr;
printf("Initial ptr points to: %d\n", *ptr);
ptr++; // Move to next element
printf("After ptr++: %d\n", *ptr);
ptr += 2; // Move 2 elements forward
printf("After ptr += 2: %d\n", *ptr);
ptr--; // Move back 1 element
printf("After ptr--: %d\n", *ptr);
ptr -= 2; // Move back 2 elements
printf("After ptr -= 2: %d\n", *ptr);
return 0;
}#include <stdio.h>
int main() {
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// Pointer to first element
int *ptr = &matrix[0][0];
printf("=== Using Single Pointer ===\n");
for (int i = 0; i < 12; i++) {
printf("%2d ", *(ptr + i));
if ((i + 1) % 4 == 0) printf("\n");
}
// Pointer to first row
int (*row_ptr)[4] = matrix;
printf("\n=== Using Row Pointer ===\n");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%2d ", row_ptr[i][j]);
}
printf("\n");
}
return 0;
}Let's say we have:
int arr[3][4];This means 3 rows and 4 columns.
Memory is contiguous — all elements are stored one after another in row-major order:
arr[0][0], arr[0][1], arr[0][2], arr[0][3],
arr[1][0], arr[1][1], arr[1][2], arr[1][3],
arr[2][0], arr[2][1], arr[2][2], arr[2][3]
You normally use:
arr[row][col]But pointers can also be used.
arr→ points to the first row (type:int (*)[4]— pointer to an array of 4 ints)arr + i→ points to the i-th row*(arr + i)→ gives the base address of the i-th row (type:int*)*(*(arr + i) + j)→ givesarr[i][j]
Example:
printf("%d", *(*(arr + 1) + 2)); // Same as arr[1][2]#include <stdio.h>
int main() {
int arr[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// Using normal indexing
printf("arr[1][2] = %d\n", arr[1][2]);
// Using pointer arithmetic
printf("*( *(arr + 1) + 2 ) = %d\n", *(*(arr + 1) + 2));
// Printing addresses
printf("arr = %p\n", (void*)arr);
printf("arr + 1 (next row) = %p\n", (void*)(arr + 1));
printf("*(arr + 1) (start of row 1) = %p\n", (void*)*(arr + 1));
printf("*(arr + 1) + 2 (3rd element in row 1) = %p\n", (void*)(*(arr + 1) + 2));
return 0;
}A 2D array in C is stored row-wise in contiguous memory.
Example:
int arr[2][3] = { {1, 2, 3}, {4, 5, 6} };Memory layout:
Address Value
1000 1
1004 2
1008 3
1012 4
1016 5
1020 6
Pointer to Array
int (*p)[3] = arr;p is a pointer to an array of 3 integers.
p + 1 → jumps 3 integers ahead.
Pointer to Pointer
int *q[2]; // Array of int pointers
q[0] = arr[0];
q[1] = arr[1];This is not the same as a 2D array in memory.
Each row can be at different memory locations (useful for dynamic allocation).
If:
int arr[2][3] = { {1, 2, 3}, {4, 5, 6} };| Access Method | Meaning |
|---|---|
arr[i][j] |
Direct indexing |
*(*(arr + i) + j) |
Pointer arithmetic |
(*(arr + i))[j] |
Array pointer dereference |
*(&arr[0][0] + i * 3 + j) |
Linear memory access |
When passing to a function, you must specify the column size:
void printArray(int arr[][3], int rows) {
for(int i = 0; i < rows; i++) {
for(int j = 0; j < 3; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
}or:
void printArray(int (*arr)[3], int rows);int **matrix = malloc(rows * sizeof(int*));
for(int i = 0; i < rows; i++)
matrix[i] = malloc(cols * sizeof(int));Each matrix[i] is separately allocated.
| Static 2D Array | Pointer to Pointer |
|---|---|
| Contiguous memory | Can be scattered in memory |
| Faster access | Slightly slower (extra indirection) |
| Requires known column size | Flexible sizes |
#include <stdio.h>
int main() {
int arr[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
int *p = &arr[0][0]; // pointer to first element
printf("arr[0][0] = %d\n", *p); // 1
printf("arr[0][1] = %d\n", *(p + 1)); // 2
printf("arr[1][0] = %d\n", *(p + 3)); // 4
return 0;
}👉 Here memory is continuous, so pointer arithmetic works like a 1D flattened array.
#include <stdio.h>
int main() {
int row1[] = {1, 2, 3};
int row2[] = {4, 5, 6};
int *arr[2]; // array of int pointers
arr[0] = row1;
arr[1] = row2;
printf("arr[0][0] = %d\n", arr[0][0]); // 1
printf("arr[0][1] = %d\n", arr[0][1]); // 2
printf("arr[1][0] = %d\n", arr[1][0]); // 4
printf("arr[1][2] = %d\n", arr[1][2]); // 6
return 0;
}👉 Here memory is not necessarily continuous. Each row can be separate, because we store row pointers.
#include <stdio.h>
#include <stdlib.h>
int main() {
int rows = 2, cols = 3;
int **arr;
// allocate row pointers
arr = (int **)malloc(rows * sizeof(int*));
for(int i=0; i<rows; i++) {
arr[i] = (int *)malloc(cols * sizeof(int));
}
// assign values
int count = 1;
for(int i=0; i<rows; i++) {
for(int j=0; j<cols; j++) {
arr[i][j] = count++;
}
}
// print
for(int i=0; i<rows; i++) {
for(int j=0; j<cols; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}👉 This is dynamic 2D array allocation using pointer to pointer.
A pointer stores the address of a variable.
A double pointer stores the address of another pointer.
👉 Example:
#include <stdio.h>
int main() {
int a = 10;
int *p = &a; // p stores the address of a
int **q = &p; // q stores the address of p
printf("a = %d\n", a);
printf("*p = %d\n", *p);
printf("**q = %d\n", **q);
return 0;
}✅ Output:
a = 10
*p = 10
**q = 10
- a → value 10 stored.
- p → stores address of a.
- q → stores address of p.
So:
*p→ value at address stored in p → a.**q→ value at address stored in *q → a.
int arr[3] = {10, 20, 30};
int *p = arr; // points to arr[0]p is a pointer to the first element.
p + 1 → points to arr[1].
You can store multiple array addresses inside a pointer-to-pointer.
#include <stdio.h>
int main() {
int a = 10, b = 20, c = 30;
int *arr[3] = {&a, &b, &c}; // array of pointers
int **pp = arr; // double pointer points to arr[0]
printf("%d\n", **pp); // prints 10
printf("%d\n", **(pp + 1)); // prints 20
printf("%d\n", **(pp + 2)); // prints 30
return 0;
}✅ Output:
10
20
30
#include <stdio.h>
int main() {
int arr[2][3] = { {1, 2, 3}, {4, 5, 6} };
int *p[2]; // array of pointers
p[0] = arr[0]; // points to row 0
p[1] = arr[1]; // points to row 1
int **pp = p; // double pointer to array of pointers
printf("%d\n", pp[0][1]); // 2
printf("%d\n", pp[1][2]); // 6
return 0;
}✅ Output:
2
6
A 2D array is actually stored in contiguous memory row by row. When you pass it to a function, it decays into a pointer to its first row.
#include <stdio.h>
// Function to print a 2D array
void printArray(int arr[][3], int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
int main() {
int arr[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
// Passing 2D array to function
printArray(arr, 2);
return 0;
}In function declaration, you must specify the number of columns ([3] here).
✅ void func(int arr[][3], int rows) → Works
❌ void func(int arr[][], int rows) → Doesn't work
Inside the function, arr is treated as a pointer to 1D arrays (rows).
So:
arr→ points to row 0 (arr[0])*(arr+i)→ points to row i*(*(arr+i) + j)→ element at row i, column j
#include <stdio.h>
int main() {
int a = 10, b = 20, c = 30, d = 40;
// Array of pointers to integers
int *ptr_array[] = {&a, &b, &c, &d};
printf("=== Array of Pointers ===\n");
for (int i = 0; i < 4; i++) {
printf("ptr_array[%d] points to: %d\n", i, *ptr_array[i]);
}
// Modifying values through array of pointers
*ptr_array[0] = 100;
*ptr_array[1] = 200;
printf("\nAfter modification:\n");
printf("a = %d, b = %d\n", a, b);
return 0;
}#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
// Pointer to an array of 5 integers
int (*array_ptr)[5] = &arr;
printf("=== Pointer to Array ===\n");
printf("Size of array_ptr: %zu bytes\n", sizeof(*array_ptr));
printf("Size of arr: %zu bytes\n", sizeof(arr));
// Accessing elements
for (int i = 0; i < 5; i++) {
printf("(*array_ptr)[%d] = %d\n", i, (*array_ptr)[i]);
}
return 0;
}#include <stdio.h>
#include <stdlib.h>
int main() {
int size;
printf("Enter array size: ");
scanf("%d", &size);
// Dynamically allocate array
int *dynamic_arr = (int*)malloc(size * sizeof(int));
if (dynamic_arr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// Initialize array
for (int i = 0; i < size; i++) {
dynamic_arr[i] = (i + 1) * 10;
}
// Display array
printf("Dynamic array: ");
for (int i = 0; i < size; i++) {
printf("%d ", dynamic_arr[i]);
}
printf("\n");
// Free allocated memory
free(dynamic_arr);
dynamic_arr = NULL;
return 0;
}#include <stdio.h>
int main() {
int arr[] = {5, 15, 25, 35, 45};
int *ptr = arr;
printf("=== Common Operations ===\n");
// Get array length (only works for arrays, not pointers)
int arr_length = sizeof(arr) / sizeof(arr[0]);
printf("Array length: %d\n", arr_length);
// Pointer arithmetic
printf("First element: %d\n", *ptr);
printf("Last element: %d\n", *(ptr + arr_length - 1));
printf("Middle element: %d\n", *(ptr + arr_length / 2));
// Compare pointers
int *end_ptr = ptr + arr_length;
printf("Elements between ptr and end_ptr:\n");
while (ptr < end_ptr) {
printf("%d ", *ptr);
ptr++;
}
printf("\n");
return 0;
}Pointers can also point to functions:
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
return a * b;
}
int main() {
int (*operation)(int, int); // Function pointer
operation = add;
printf("Addition: %d\n", operation(5, 3));
operation = multiply;
printf("Multiplication: %d\n", operation(5, 3));
return 0;
}int *ptr; // Bad - uninitialized pointer
int *ptr = NULL; // Good - initialized to NULLif (ptr != NULL) {
printf("Value: %d\n", *ptr);
}int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;
// Don't go beyond array bounds
// *(ptr + 10) would be dangerous!When working with pointers, be mindful of memory:
#include <stdlib.h>
int main() {
// Dynamic memory allocation
int *ptr = (int*)malloc(sizeof(int));
if (ptr != NULL) {
*ptr = 42;
printf("Value: %d\n", *ptr);
// Always free allocated memory
free(ptr);
ptr = NULL; // Good practice after freeing
}
return 0;
}Pointers are essential in C programming for:
- Efficient memory management
- Dynamic data structures
- Function pointers
- Array manipulation
- Passing parameters by reference
Understanding pointers is crucial for becoming proficient in C programming and understanding how memory works at a low level.
🧭 Navigation
⬅️ Previous | 🏠 Home | ➡️ Next