C++ Pointers
Working with memory addresses and indirect access
👉 What are C++ Pointers?
C++ pointers are variables that store memory addresses of other variables, enabling dynamic memory allocation, efficient parameter passing, and powerful data structure implementations like linked lists.
// Simple pointer example
int x = 42;
int* ptr = &x // ptr stores address of x
Pointer Concepts
Declaration
Declare pointers with * symbol
int* ptr;
char* charPtr;
Address Operator
Get address with & operator
int x = 10;
int* ptr = &x
Dereference
Access value with * operator
int value = *ptr;
*ptr = 20;
Null Pointers
Pointers that don't point anywhere
int* ptr = nullptr;
if (ptr != nullptr) {}
🔹 Basic Pointer Example
A pointer is a variable that stores the memory address of another variable, enabling indirect access and
manipulation of data. Declared with an asterisk (e.g., int* ptr), it is initialized using
the address-of operator (&). Dereferencing with (*) accesses the value at that address. This fundamental mechanism
is used for dynamic memory allocation, array traversal, and implementing data structures like linked lists, forming
the basis for efficient low-level memory management.
#include <iostream>
using namespace std;
int main() {
int number = 42;
int* ptr = &number // ptr stores address of number
cout << "Value of number: " << number << endl;
cout << "Address of number: " << &number << endl;
cout << "Value stored in ptr: " << ptr << endl;
cout << "Value pointed to by ptr: " << *ptr << endl;
// Modify value through pointer
*ptr = 100;
cout << "\nAfter modifying through pointer:" << endl;
cout << "Value of number: " << number << endl;
cout << "Value pointed to by ptr: " << *ptr << endl;
return 0;
}
Output:
Value of number: 42 Address of number: 0x7fff5fbff6ac Value stored in ptr: 0x7fff5fbff6ac Value pointed to by ptr: 42 After modifying through pointer: Value of number: 100 Value pointed to by ptr: 100
🔹 Pointer Arithmetic
Pointer arithmetic allows incrementing or decrementing a pointer to navigate through contiguous memory blocks, such as arrays. Adding 1 to an integer pointer advances it by the size of an integer (e.g., 4 bytes). This enables efficient iteration over array elements without indexing. Operations include addition, subtraction, and comparison. It's a powerful but potentially dangerous feature that requires careful bounds checking to avoid accessing invalid memory locations and causing segmentation faults.
#include <iostream>
using namespace std;
int main() {
int arr[] = {10, 20, 30, 40, 50};
int* ptr = arr; // Points to first element
cout << "Array elements using pointer arithmetic:" << endl;
for (int i = 0; i < 5; i++) {
cout << "Element " << i << ": " << *ptr << endl;
cout << "Address: " << ptr << endl;
ptr++; // Move to next element
}
// Reset pointer
ptr = arr;
cout << "\nUsing array notation vs pointer notation:" << endl;
for (int i = 0; i < 5; i++) {
cout << "arr[" << i << "] = " << arr[i];
cout << ", *(ptr + " << i << ") = " << *(ptr + i) << endl;
}
return 0;
}
Output:
Array elements using pointer arithmetic: Element 0: 10 Address: 0x7fff5fbff6a0 Element 1: 20 Address: 0x7fff5fbff6a4 Element 2: 30 Address: 0x7fff5fbff6a8 Element 3: 40 Address: 0x7fff5fbff6ac Element 4: 50 Address: 0x7fff5fbff6b0 Using array notation vs pointer notation: arr[0] = 10, *(ptr + 0) = 10 arr[1] = 20, *(ptr + 1) = 20 arr[2] = 30, *(ptr + 2) = 30 arr[3] = 40, *(ptr + 3) = 40 arr[4] = 50, *(ptr + 4) = 50
🔹 Pointers and Functions
Pointers allow functions to directly modify variables outside their own scope by accessing memory
addresses. Instead of passing a copy of a variable, you pass its address using the &
operator. Inside the function, dereferencing the pointer with * lets you read or change the original
value. This technique, known as "pass-by-pointer," is essential for functions that need to update multiple values or
work with large data structures efficiently, avoiding costly copying.
#include <iostream>
using namespace std;
// Function that takes pointer parameter
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
// Function that returns a pointer
int* findMax(int* arr, int size) {
int* maxPtr = arr;
for (int i = 1; i < size; i++) {
if (*(arr + i) > *maxPtr) {
maxPtr = arr + i;
}
}
return maxPtr;
}
int main() {
int x = 10, y = 20;
cout << "Before swap: x = " << x << ", y = " << y << endl;
swap(&x, &y);
cout << "After swap: x = " << x << ", y = " << y << endl;
int numbers[] = {5, 2, 8, 1, 9, 3};
int size = sizeof(numbers) / sizeof(numbers[0]);
int* maxPtr = findMax(numbers, size);
cout << "Maximum value: " << *maxPtr << endl;
cout << "Maximum at index: " << (maxPtr - numbers) << endl;
return 0;
}
Output:
Before swap: x = 10, y = 20 After swap: x = 20, y = 10 Maximum value: 9 Maximum at index: 4
🔹 Dynamic Memory Allocation
Dynamic memory allocation lets you request memory at runtime using new and release it with
delete. Unlike stack variables, dynamically allocated memory persists until explicitly
freed, allowing flexible data structures like linked lists or arrays of unknown size. Always pair each
new with a delete to prevent memory leaks. For arrays, use new[] and
delete[]. Proper management ensures efficient resource use and prevents crashes or performance
degradation.
#include <iostream>
using namespace std;
int main() {
// Allocate memory for single integer
int* ptr = new int(42);
cout << "Value: " << *ptr << endl;
cout << "Address: " << ptr << endl;
// Allocate memory for array
int size = 5;
int* arr = new int[size];
// Initialize array
for (int i = 0; i < size; i++) {
arr[i] = (i + 1) * 10;
}
cout << "Dynamic array: ";
for (int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
cout << endl;
// Free memory
delete ptr; // For single variable
delete[] arr; // For arrays
// Set pointers to nullptr after deletion
ptr = nullptr;
arr = nullptr;
cout << "Memory freed successfully!" << endl;
return 0;
}
Output:
Value: 42 Address: 0x55f8a7c6feb0 Dynamic array: 10 20 30 40 50 Memory freed successfully!