Arrays, Pointers and Functions
Understanding pointers is fundamental to mastering C++ programming. Pointers provide direct access to memory addresses, enabling efficient manipulation of data structures, function parameters, and dynamic memory allocation. This guide explores how pointers work with arrays and functions, and why they are essential for writing efficient C++ code.
What are Pointers?
A pointer is a variable that stores the memory address of another variable. Instead of holding a value directly, a pointer holds the location where a value is stored in memory.
Basic Pointer Syntax
int x = 10; // Regular variable
int* ptr = &x; // Pointer to x (stores address of x)
// ptr now contains the memory address where x is stored
// *ptr dereferences the pointer to access the value (10)
Key Pointer Operations
&(address-of operator): Gets the memory address of a variable*(dereference operator): Accesses the value at the address stored in the pointerptr: The pointer variable itself (contains an address)*ptr: The value at the address stored in ptr
Why Are Pointers Important?
- Memory Efficiency: Pass large data structures by reference instead of copying entire objects
- Dynamic Memory Allocation: Allocate and deallocate memory at runtime using
newanddelete - Array Manipulation: Efficiently traverse and manipulate arrays
- Function Parameters: Modify variables passed to functions (pass by reference)
- Data Structures: Essential for implementing linked lists, trees, and graphs
- Performance: Avoid expensive copy operations, especially with large objects
Pointers and Arrays
In C++, arrays and pointers are closely related. The name of an array is essentially a pointer to its first element.
Array-Pointer Relationship
int arr[5] = {10, 20, 30, 40, 50};
// These are equivalent:
int* ptr1 = arr; // Array name is a pointer to first element
int* ptr2 = &arr[0]; // Explicitly get address of first element
// Array indexing using pointers:
arr[2] // Access element at index 2
*(arr + 2) // Same as arr[2] using pointer arithmetic
ptr1[2] // Same as arr[2]
*(ptr1 + 2) // Same as arr[2]
Pointer Arithmetic
When you add an integer to a pointer, it moves forward by that many elements (not bytes). The compiler automatically accounts for the size of the data type.
int arr[5] = {10, 20, 30, 40, 50};
int* ptr = arr;
ptr + 0 // Points to arr[0] (value: 10)
ptr + 1 // Points to arr[1] (value: 20)
ptr + 2 // Points to arr[2] (value: 30)
*(ptr + 2) // Dereferences to get value 30
Visual Representation
Array and Pointer Relationship
Pointers and Functions
Pointers enable functions to modify variables passed as arguments and efficiently handle arrays and large data structures.
Pass by Value vs Pass by Reference
// Pass by Value (creates a copy)
void incrementByValue(int x) {
x++; // Only modifies the copy
}
// Pass by Pointer (modifies original)
void incrementByPointer(int* x) {
(*x)++; // Modifies the original variable
}
// Pass by Reference (C++ style, cleaner)
void incrementByReference(int& x) {
x++; // Modifies the original variable
}
int num = 5;
incrementByValue(num); // num is still 5
incrementByPointer(&num); // num becomes 6
incrementByReference(num); // num becomes 7
Passing Arrays to Functions
When you pass an array to a function, you're actually passing a pointer to the first element. Arrays are always passed by reference (pointer).
// These function signatures are equivalent:
void printArray(int arr[], int size);
void printArray(int* arr, int size);
void printArray(int* arr, int size) {
for (int i = 0; i < size; i++) {
cout << arr[i] << " "; // Can use array notation
// or
cout << *(arr + i) << " "; // Can use pointer notation
}
}
int numbers[5] = {1, 2, 3, 4, 5};
printArray(numbers, 5); // Pass array name (pointer)
Returning Pointers from Functions
// Return pointer to dynamically allocated memory
int* createArray(int size) {
int* arr = new int[size];
for (int i = 0; i < size; i++) {
arr[i] = i * 2;
}
return arr; // Return pointer to first element
}
int* myArray = createArray(10);
// Use myArray...
delete[] myArray; // Don't forget to free memory!
Function Pointers
Pointers can also point to functions, enabling dynamic function calls and callbacks.
// Function pointer syntax
int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }
int (*operation)(int, int); // Function pointer declaration
operation = add; // Point to add function
int result1 = operation(5, 3); // Calls add(5, 3) → 8
operation = multiply; // Point to multiply function
int result2 = operation(5, 3); // Calls multiply(5, 3) → 15
Common Pointer Pitfalls
- Dangling Pointers: Pointers that reference memory that has been freed
- Memory Leaks: Forgetting to free dynamically allocated memory
- Null Pointer Dereference: Accessing memory through a null pointer
- Uninitialized Pointers: Using pointers before assigning them a valid address
- Array Bounds: Accessing array elements outside valid range
Best Practices
- Always initialize pointers (use
nullptrif not immediately assigned) - Check for null pointers before dereferencing
- Use smart pointers (
unique_ptr,shared_ptr) in modern C++ - Match every
newwith adelete, andnew[]withdelete[] - Prefer references over pointers when you don't need nullability or reassignment
- Use const pointers when you don't need to modify the pointed-to value